@djangocfg/ui-tools 2.1.268 → 2.1.271
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.
- package/dist/PlaygroundLayout-G325I6HM.mjs +736 -0
- package/dist/PlaygroundLayout-G325I6HM.mjs.map +1 -0
- package/dist/PlaygroundLayout-ZO2LO7M5.cjs +743 -0
- package/dist/PlaygroundLayout-ZO2LO7M5.cjs.map +1 -0
- package/dist/{PrettyCode.client-OO3KAJSM.mjs → PrettyCode.client-DW5LTG47.mjs} +5 -5
- package/dist/PrettyCode.client-DW5LTG47.mjs.map +1 -0
- package/dist/{PrettyCode.client-V2ZN5DTH.cjs → PrettyCode.client-SGDGQTYT.cjs} +5 -5
- package/dist/PrettyCode.client-SGDGQTYT.cjs.map +1 -0
- package/dist/{chunk-SZ2CZEQZ.mjs → chunk-QZ55LYK2.mjs} +141 -169
- package/dist/chunk-QZ55LYK2.mjs.map +1 -0
- package/dist/{chunk-CRHHUOVJ.cjs → chunk-WM4RT5KX.cjs} +139 -169
- package/dist/chunk-WM4RT5KX.cjs.map +1 -0
- package/dist/index.cjs +8 -8
- package/dist/index.mjs +5 -5
- package/package.json +6 -6
- package/src/tools/OpenapiViewer/README.md +121 -0
- package/src/tools/OpenapiViewer/components/PlaygroundLayout/EndpointList.tsx +228 -0
- package/src/tools/OpenapiViewer/components/PlaygroundLayout/RequestPanel.tsx +258 -0
- package/src/tools/OpenapiViewer/components/PlaygroundLayout/ResponsePanel.tsx +127 -0
- package/src/tools/OpenapiViewer/components/PlaygroundLayout/index.tsx +107 -0
- package/src/tools/OpenapiViewer/components/PlaygroundLayout/ui.tsx +137 -0
- package/src/tools/OpenapiViewer/components/index.ts +0 -9
- package/src/tools/OpenapiViewer/context/PlaygroundContext.tsx +198 -208
- package/src/tools/OpenapiViewer/types.ts +1 -0
- package/src/tools/PrettyCode/PrettyCode.client.tsx +17 -12
- package/dist/PlaygroundLayout-FKXSULJ3.cjs +0 -971
- package/dist/PlaygroundLayout-FKXSULJ3.cjs.map +0 -1
- package/dist/PlaygroundLayout-XMMHPZYP.mjs +0 -964
- package/dist/PlaygroundLayout-XMMHPZYP.mjs.map +0 -1
- package/dist/PrettyCode.client-OO3KAJSM.mjs.map +0 -1
- package/dist/PrettyCode.client-V2ZN5DTH.cjs.map +0 -1
- package/dist/chunk-CRHHUOVJ.cjs.map +0 -1
- package/dist/chunk-SZ2CZEQZ.mjs.map +0 -1
- package/src/tools/OpenapiViewer/components/EndpointInfo.tsx +0 -149
- package/src/tools/OpenapiViewer/components/EndpointsLibrary.tsx +0 -278
- package/src/tools/OpenapiViewer/components/PlaygroundLayout.tsx +0 -91
- package/src/tools/OpenapiViewer/components/PlaygroundStepper.tsx +0 -100
- package/src/tools/OpenapiViewer/components/RequestBuilder.tsx +0 -157
- package/src/tools/OpenapiViewer/components/RequestParametersForm.tsx +0 -253
- package/src/tools/OpenapiViewer/components/ResponseViewer.tsx +0 -173
- package/src/tools/OpenapiViewer/components/VersionSelector.tsx +0 -68
|
@@ -0,0 +1,743 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkWM4RT5KX_cjs = require('./chunk-WM4RT5KX.cjs');
|
|
4
|
+
var chunk33AMWFBZ_cjs = require('./chunk-33AMWFBZ.cjs');
|
|
5
|
+
require('./chunk-2SMCH62O.cjs');
|
|
6
|
+
var chunkWGEGR3DF_cjs = require('./chunk-WGEGR3DF.cjs');
|
|
7
|
+
var React4 = require('react');
|
|
8
|
+
var lib = require('@djangocfg/ui-core/lib');
|
|
9
|
+
var hooks = require('@djangocfg/ui-core/hooks');
|
|
10
|
+
var lucideReact = require('lucide-react');
|
|
11
|
+
var components = require('@djangocfg/ui-core/components');
|
|
12
|
+
var consola = require('consola');
|
|
13
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
14
|
+
|
|
15
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
|
+
|
|
17
|
+
var React4__default = /*#__PURE__*/_interopDefault(React4);
|
|
18
|
+
var consola__default = /*#__PURE__*/_interopDefault(consola);
|
|
19
|
+
|
|
20
|
+
var useMobile = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(() => {
|
|
21
|
+
const isMobile = hooks.useIsMobile();
|
|
22
|
+
return {
|
|
23
|
+
isMobile,
|
|
24
|
+
isDesktop: !isMobile
|
|
25
|
+
};
|
|
26
|
+
}, "useMobile");
|
|
27
|
+
var HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
|
|
28
|
+
var extractEndpoints = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((schema) => {
|
|
29
|
+
const endpoints = [];
|
|
30
|
+
if (!schema.paths) return [];
|
|
31
|
+
const baseUrl = schema.servers && schema.servers.length > 0 ? schema.servers[0].url : "";
|
|
32
|
+
for (const [path, methods] of Object.entries(schema.paths)) {
|
|
33
|
+
for (const method of HTTP_METHODS) {
|
|
34
|
+
const op = methods[method];
|
|
35
|
+
if (!op) continue;
|
|
36
|
+
const methodUpper = method.toUpperCase();
|
|
37
|
+
const description = op.description || op.summary || `${methodUpper} ${path}`;
|
|
38
|
+
const category = op.tags?.[0] || "Other";
|
|
39
|
+
const parameters = [];
|
|
40
|
+
const allParams = [...methods.parameters || [], ...op.parameters || []];
|
|
41
|
+
for (const param of allParams) {
|
|
42
|
+
parameters.push({
|
|
43
|
+
name: param.name,
|
|
44
|
+
type: param.schema?.type || "string",
|
|
45
|
+
required: param.required || false,
|
|
46
|
+
description: param.description
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const responses = [];
|
|
50
|
+
if (op.responses) {
|
|
51
|
+
for (const [code, response] of Object.entries(op.responses)) {
|
|
52
|
+
responses.push({
|
|
53
|
+
code,
|
|
54
|
+
description: response.description || `Response ${code}`
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
let requestBody;
|
|
59
|
+
if (op.requestBody) {
|
|
60
|
+
const content = op.requestBody.content;
|
|
61
|
+
const mediaType = content?.["application/json"] || content?.[Object.keys(content || {})[0]];
|
|
62
|
+
requestBody = {
|
|
63
|
+
type: mediaType?.schema?.type || "object",
|
|
64
|
+
description: op.requestBody.description
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const endpoint = {
|
|
68
|
+
name: path.split("/").pop() || path,
|
|
69
|
+
method: methodUpper,
|
|
70
|
+
path: baseUrl + path,
|
|
71
|
+
description,
|
|
72
|
+
category,
|
|
73
|
+
parameters: parameters.length > 0 ? parameters : void 0,
|
|
74
|
+
requestBody,
|
|
75
|
+
responses: responses.length > 0 ? responses : void 0
|
|
76
|
+
};
|
|
77
|
+
endpoints.push(endpoint);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return endpoints;
|
|
81
|
+
}, "extractEndpoints");
|
|
82
|
+
var getCategories = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((endpoints) => {
|
|
83
|
+
const categories = /* @__PURE__ */ new Set();
|
|
84
|
+
endpoints.forEach((endpoint) => categories.add(endpoint.category));
|
|
85
|
+
return Array.from(categories).sort();
|
|
86
|
+
}, "getCategories");
|
|
87
|
+
var fetchSchema = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(async (url) => {
|
|
88
|
+
const response = await fetch(url, {
|
|
89
|
+
headers: {
|
|
90
|
+
"Accept": "application/json"
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
throw new Error(`Failed to fetch schema: ${response.statusText}`);
|
|
95
|
+
}
|
|
96
|
+
return response.json();
|
|
97
|
+
}, "fetchSchema");
|
|
98
|
+
function useOpenApiSchema({
|
|
99
|
+
schemas,
|
|
100
|
+
defaultSchemaId
|
|
101
|
+
}) {
|
|
102
|
+
const [loading, setLoading] = React4.useState(true);
|
|
103
|
+
const [error, setError] = React4.useState(null);
|
|
104
|
+
const [currentSchemaId, setCurrentSchemaId] = React4.useState(
|
|
105
|
+
defaultSchemaId || schemas[0]?.id
|
|
106
|
+
);
|
|
107
|
+
const [loadedSchemas, setLoadedSchemas] = React4.useState(
|
|
108
|
+
/* @__PURE__ */ new Map()
|
|
109
|
+
);
|
|
110
|
+
const currentSchema = React4.useMemo(
|
|
111
|
+
() => schemas.find((s) => s.id === currentSchemaId) || null,
|
|
112
|
+
[schemas, currentSchemaId]
|
|
113
|
+
);
|
|
114
|
+
const currentOpenApiSchema = React4.useMemo(
|
|
115
|
+
() => currentSchemaId ? loadedSchemas.get(currentSchemaId) : null,
|
|
116
|
+
[loadedSchemas, currentSchemaId]
|
|
117
|
+
);
|
|
118
|
+
const endpoints = React4.useMemo(
|
|
119
|
+
() => currentOpenApiSchema ? extractEndpoints(currentOpenApiSchema) : [],
|
|
120
|
+
[currentOpenApiSchema]
|
|
121
|
+
);
|
|
122
|
+
const categories = React4.useMemo(() => getCategories(endpoints), [endpoints]);
|
|
123
|
+
React4.useEffect(() => {
|
|
124
|
+
if (!currentSchema) return;
|
|
125
|
+
if (loadedSchemas.has(currentSchema.id)) {
|
|
126
|
+
setLoading(false);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
setLoading(true);
|
|
130
|
+
setError(null);
|
|
131
|
+
fetchSchema(currentSchema.url).then((schema) => {
|
|
132
|
+
setLoadedSchemas((prev) => new Map(prev).set(currentSchema.id, schema));
|
|
133
|
+
consola__default.default.success(`Schema loaded: ${currentSchema.name}`);
|
|
134
|
+
setLoading(false);
|
|
135
|
+
}).catch((err) => {
|
|
136
|
+
consola__default.default.error(`Error loading schema from ${currentSchema.url}:`, err);
|
|
137
|
+
setError(err instanceof Error ? err.message : "Failed to load schema");
|
|
138
|
+
setLoading(false);
|
|
139
|
+
});
|
|
140
|
+
}, [currentSchema, loadedSchemas]);
|
|
141
|
+
const setCurrentSchema = React4.useCallback((schemaId) => {
|
|
142
|
+
setCurrentSchemaId(schemaId);
|
|
143
|
+
}, []);
|
|
144
|
+
const refresh = React4.useCallback(() => {
|
|
145
|
+
if (!currentSchema) return;
|
|
146
|
+
setLoading(true);
|
|
147
|
+
setError(null);
|
|
148
|
+
setLoadedSchemas((prev) => {
|
|
149
|
+
const next = new Map(prev);
|
|
150
|
+
next.delete(currentSchema.id);
|
|
151
|
+
return next;
|
|
152
|
+
});
|
|
153
|
+
fetchSchema(currentSchema.url).then((schema) => {
|
|
154
|
+
setLoadedSchemas((prev) => new Map(prev).set(currentSchema.id, schema));
|
|
155
|
+
consola__default.default.success(`Schema refreshed: ${currentSchema.name}`);
|
|
156
|
+
setLoading(false);
|
|
157
|
+
}).catch((err) => {
|
|
158
|
+
consola__default.default.error(`Error refreshing schema from ${currentSchema.url}:`, err);
|
|
159
|
+
setError(err instanceof Error ? err.message : "Failed to refresh schema");
|
|
160
|
+
setLoading(false);
|
|
161
|
+
});
|
|
162
|
+
}, [currentSchema]);
|
|
163
|
+
return {
|
|
164
|
+
loading,
|
|
165
|
+
error,
|
|
166
|
+
endpoints,
|
|
167
|
+
categories,
|
|
168
|
+
schemas,
|
|
169
|
+
currentSchema,
|
|
170
|
+
setCurrentSchema,
|
|
171
|
+
refresh
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
chunkWGEGR3DF_cjs.__name(useOpenApiSchema, "useOpenApiSchema");
|
|
175
|
+
var METHOD_STYLES = {
|
|
176
|
+
GET: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/25",
|
|
177
|
+
POST: "bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/25",
|
|
178
|
+
PUT: "bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/25",
|
|
179
|
+
PATCH: "bg-orange-500/10 text-orange-600 dark:text-orange-400 border-orange-500/25",
|
|
180
|
+
DELETE: "bg-red-500/10 text-red-600 dark:text-red-400 border-red-500/25"
|
|
181
|
+
};
|
|
182
|
+
var METHOD_FALLBACK = "bg-muted text-muted-foreground border-border";
|
|
183
|
+
function getMethodStyle(method) {
|
|
184
|
+
return METHOD_STYLES[method.toUpperCase()] ?? METHOD_FALLBACK;
|
|
185
|
+
}
|
|
186
|
+
chunkWGEGR3DF_cjs.__name(getMethodStyle, "getMethodStyle");
|
|
187
|
+
function getStatusStyle(status) {
|
|
188
|
+
if (status >= 500) return "bg-red-500/10 text-red-500 dark:text-red-400 border-red-500/25";
|
|
189
|
+
if (status >= 400) return "bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/25";
|
|
190
|
+
if (status >= 300) return "bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/25";
|
|
191
|
+
return "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/25";
|
|
192
|
+
}
|
|
193
|
+
chunkWGEGR3DF_cjs.__name(getStatusStyle, "getStatusStyle");
|
|
194
|
+
function relativePath(full) {
|
|
195
|
+
try {
|
|
196
|
+
return new URL(full).pathname;
|
|
197
|
+
} catch {
|
|
198
|
+
return full;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
chunkWGEGR3DF_cjs.__name(relativePath, "relativePath");
|
|
202
|
+
function MethodBadge({ method }) {
|
|
203
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: lib.cn(
|
|
204
|
+
"inline-flex shrink-0 items-center rounded border px-1.5 py-px",
|
|
205
|
+
"font-mono text-[10px] font-bold uppercase tracking-wider leading-none",
|
|
206
|
+
getMethodStyle(method)
|
|
207
|
+
), children: method });
|
|
208
|
+
}
|
|
209
|
+
chunkWGEGR3DF_cjs.__name(MethodBadge, "MethodBadge");
|
|
210
|
+
function StatusBadge({ status }) {
|
|
211
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: lib.cn(
|
|
212
|
+
"inline-flex items-center rounded border px-1.5 py-px",
|
|
213
|
+
"font-mono text-[11px] font-bold leading-none",
|
|
214
|
+
getStatusStyle(status)
|
|
215
|
+
), children: status });
|
|
216
|
+
}
|
|
217
|
+
chunkWGEGR3DF_cjs.__name(StatusBadge, "StatusBadge");
|
|
218
|
+
function SectionLabel({ children }) {
|
|
219
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/60 select-none", children });
|
|
220
|
+
}
|
|
221
|
+
chunkWGEGR3DF_cjs.__name(SectionLabel, "SectionLabel");
|
|
222
|
+
function Panel({ children, className }) {
|
|
223
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: lib.cn("flex flex-col min-h-0 overflow-hidden", className), children });
|
|
224
|
+
}
|
|
225
|
+
chunkWGEGR3DF_cjs.__name(Panel, "Panel");
|
|
226
|
+
function ScrollArea({ children, className }) {
|
|
227
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: lib.cn("flex-1 overflow-y-auto min-h-0", className), children });
|
|
228
|
+
}
|
|
229
|
+
chunkWGEGR3DF_cjs.__name(ScrollArea, "ScrollArea");
|
|
230
|
+
function PanelHeader({ title }) {
|
|
231
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 border-b px-4 h-10 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-semibold uppercase tracking-widest text-muted-foreground/50", children: title }) });
|
|
232
|
+
}
|
|
233
|
+
chunkWGEGR3DF_cjs.__name(PanelHeader, "PanelHeader");
|
|
234
|
+
function EmptyState({ icon: Icon, text }) {
|
|
235
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center h-full gap-3 px-6 text-center", children: [
|
|
236
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-7 w-7 text-muted-foreground/25" }),
|
|
237
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: text })
|
|
238
|
+
] });
|
|
239
|
+
}
|
|
240
|
+
chunkWGEGR3DF_cjs.__name(EmptyState, "EmptyState");
|
|
241
|
+
function CollapsibleSection({
|
|
242
|
+
label,
|
|
243
|
+
action,
|
|
244
|
+
children,
|
|
245
|
+
defaultOpen = false
|
|
246
|
+
}) {
|
|
247
|
+
const [open, setOpen] = React4__default.default.useState(defaultOpen);
|
|
248
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0", children: [
|
|
249
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
250
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
251
|
+
"button",
|
|
252
|
+
{
|
|
253
|
+
type: "button",
|
|
254
|
+
onClick: () => setOpen((v) => !v),
|
|
255
|
+
className: "flex items-center gap-1.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/60 hover:text-muted-foreground transition-colors py-1",
|
|
256
|
+
children: [
|
|
257
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: lib.cn("h-3 w-3 transition-transform", open && "rotate-90") }),
|
|
258
|
+
label
|
|
259
|
+
]
|
|
260
|
+
}
|
|
261
|
+
),
|
|
262
|
+
action && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: action })
|
|
263
|
+
] }),
|
|
264
|
+
open && /* @__PURE__ */ jsxRuntime.jsx("div", { children })
|
|
265
|
+
] });
|
|
266
|
+
}
|
|
267
|
+
chunkWGEGR3DF_cjs.__name(CollapsibleSection, "CollapsibleSection");
|
|
268
|
+
function EndpointRow({
|
|
269
|
+
method,
|
|
270
|
+
path,
|
|
271
|
+
description,
|
|
272
|
+
isActive,
|
|
273
|
+
onClick
|
|
274
|
+
}) {
|
|
275
|
+
const displayPath = relativePath(path);
|
|
276
|
+
const rowCls = lib.cn(
|
|
277
|
+
"group w-full text-left flex items-start gap-2.5 px-3 py-2.5 transition-colors hover:bg-muted/40",
|
|
278
|
+
isActive && "bg-primary/[0.06] hover:bg-primary/[0.09]"
|
|
279
|
+
);
|
|
280
|
+
const arrowCls = lib.cn(
|
|
281
|
+
"h-3.5 w-3.5 shrink-0 mt-px transition-opacity",
|
|
282
|
+
isActive ? "text-primary opacity-100" : "opacity-0 group-hover:opacity-30"
|
|
283
|
+
);
|
|
284
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("button", { className: rowCls, onClick, children: [
|
|
285
|
+
/* @__PURE__ */ jsxRuntime.jsx(MethodBadge, { method }),
|
|
286
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
287
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-mono text-[11px] text-foreground/75 truncate leading-tight", children: displayPath }),
|
|
288
|
+
description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] text-muted-foreground/60 truncate leading-tight mt-0.5", children: description })
|
|
289
|
+
] }),
|
|
290
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: arrowCls })
|
|
291
|
+
] });
|
|
292
|
+
}
|
|
293
|
+
chunkWGEGR3DF_cjs.__name(EndpointRow, "EndpointRow");
|
|
294
|
+
function EndpointList() {
|
|
295
|
+
const { state, config, setSelectedEndpoint, setSelectedCategory, setSearchTerm } = chunkWM4RT5KX_cjs.usePlaygroundContext();
|
|
296
|
+
const { endpoints, categories, loading, error, schemas, currentSchema, setCurrentSchema } = useOpenApiSchema({ schemas: config.schemas, defaultSchemaId: config.defaultSchemaId });
|
|
297
|
+
const [debouncedSearch, setDebouncedSearch] = React4.useState(state.searchTerm);
|
|
298
|
+
React4.useEffect(() => {
|
|
299
|
+
const id = setTimeout(() => setDebouncedSearch(state.searchTerm), 150);
|
|
300
|
+
return () => clearTimeout(id);
|
|
301
|
+
}, [state.searchTerm]);
|
|
302
|
+
const schemaOptions = React4.useMemo(
|
|
303
|
+
() => schemas.map((s) => ({ value: s.id, label: s.name })),
|
|
304
|
+
[schemas]
|
|
305
|
+
);
|
|
306
|
+
const filtered = React4.useMemo(() => {
|
|
307
|
+
let list = chunkWM4RT5KX_cjs.deduplicateEndpoints(endpoints, state.selectedVersion);
|
|
308
|
+
if (state.selectedCategory !== "All") {
|
|
309
|
+
list = list.filter((e) => e.category === state.selectedCategory);
|
|
310
|
+
}
|
|
311
|
+
if (debouncedSearch) {
|
|
312
|
+
const q = debouncedSearch.toLowerCase();
|
|
313
|
+
list = list.filter(
|
|
314
|
+
(e) => e.name.toLowerCase().includes(q) || e.description.toLowerCase().includes(q) || e.path.toLowerCase().includes(q)
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
return list;
|
|
318
|
+
}, [endpoints, state.selectedCategory, debouncedSearch, state.selectedVersion]);
|
|
319
|
+
const isFiltered = state.selectedCategory !== "All";
|
|
320
|
+
const hasCategories = categories.length > 0;
|
|
321
|
+
const hasMultipleSchemas = schemas.length > 1;
|
|
322
|
+
const endpointLabel = `${filtered.length} endpoint${filtered.length !== 1 ? "s" : ""}`;
|
|
323
|
+
const downloadFilename = currentSchema ? `${currentSchema.id}-openapi.json` : "openapi.json";
|
|
324
|
+
if (loading) {
|
|
325
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 space-y-1.5", children: Array.from({ length: 12 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(components.Skeleton, { className: "h-10 w-full rounded" }, i)) });
|
|
326
|
+
}
|
|
327
|
+
if (error) {
|
|
328
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-destructive", children: [
|
|
329
|
+
"Failed to load schema: ",
|
|
330
|
+
error
|
|
331
|
+
] }) });
|
|
332
|
+
}
|
|
333
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
334
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 border-b px-2.5 py-2 space-y-2", children: [
|
|
335
|
+
hasMultipleSchemas && /* @__PURE__ */ jsxRuntime.jsx(
|
|
336
|
+
components.Combobox,
|
|
337
|
+
{
|
|
338
|
+
options: schemaOptions,
|
|
339
|
+
value: currentSchema?.id ?? "",
|
|
340
|
+
onValueChange: (id) => id && setCurrentSchema(id),
|
|
341
|
+
placeholder: "Select API",
|
|
342
|
+
searchPlaceholder: "Search APIs\u2026",
|
|
343
|
+
emptyText: "No APIs found",
|
|
344
|
+
className: "w-full h-8 text-xs"
|
|
345
|
+
}
|
|
346
|
+
),
|
|
347
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5", children: [
|
|
348
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 min-w-0", children: [
|
|
349
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground/50 pointer-events-none" }),
|
|
350
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
351
|
+
components.Input,
|
|
352
|
+
{
|
|
353
|
+
placeholder: "Search endpoints\u2026",
|
|
354
|
+
value: state.searchTerm,
|
|
355
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
356
|
+
className: "pl-8 h-8 text-xs"
|
|
357
|
+
}
|
|
358
|
+
)
|
|
359
|
+
] }),
|
|
360
|
+
hasCategories && /* @__PURE__ */ jsxRuntime.jsxs(components.DropdownMenu, { children: [
|
|
361
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs("button", { className: lib.cn(
|
|
362
|
+
"relative shrink-0 flex items-center justify-center h-8 w-8 rounded-md border transition-colors",
|
|
363
|
+
isFiltered ? "border-primary bg-primary/10 text-primary" : "border-input bg-background text-muted-foreground hover:text-foreground hover:bg-muted/50"
|
|
364
|
+
), children: [
|
|
365
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: "h-3.5 w-3.5" }),
|
|
366
|
+
isFiltered && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-1 -right-1 h-2 w-2 rounded-full bg-primary" })
|
|
367
|
+
] }) }),
|
|
368
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.DropdownMenuContent, { align: "end", className: "min-w-[160px] max-h-72 overflow-y-auto", children: ["All", ...categories].map((c) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
369
|
+
components.DropdownMenuItem,
|
|
370
|
+
{
|
|
371
|
+
onClick: () => setSelectedCategory(c),
|
|
372
|
+
className: lib.cn("text-xs", state.selectedCategory === c && "bg-accent font-medium"),
|
|
373
|
+
children: c
|
|
374
|
+
},
|
|
375
|
+
c
|
|
376
|
+
)) })
|
|
377
|
+
] })
|
|
378
|
+
] })
|
|
379
|
+
] }),
|
|
380
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 flex items-center justify-between px-3 py-1 border-b bg-muted/20", children: [
|
|
381
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground/50 tabular-nums", children: endpointLabel }),
|
|
382
|
+
currentSchema && /* @__PURE__ */ jsxRuntime.jsx(
|
|
383
|
+
components.DownloadButton,
|
|
384
|
+
{
|
|
385
|
+
url: currentSchema.url,
|
|
386
|
+
filename: downloadFilename,
|
|
387
|
+
variant: "ghost",
|
|
388
|
+
size: "sm",
|
|
389
|
+
className: "h-6 px-2 text-[10px] text-muted-foreground/50 hover:text-foreground",
|
|
390
|
+
children: "JSON"
|
|
391
|
+
}
|
|
392
|
+
)
|
|
393
|
+
] }),
|
|
394
|
+
/* @__PURE__ */ jsxRuntime.jsx(ScrollArea, { children: filtered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-10 text-center text-xs text-muted-foreground", children: "No endpoints found" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-border/40", children: filtered.map((ep) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
395
|
+
EndpointRow,
|
|
396
|
+
{
|
|
397
|
+
method: ep.method,
|
|
398
|
+
path: ep.path,
|
|
399
|
+
description: ep.description,
|
|
400
|
+
isActive: state.selectedEndpoint?.path === ep.path && state.selectedEndpoint?.method === ep.method,
|
|
401
|
+
onClick: () => setSelectedEndpoint(ep)
|
|
402
|
+
},
|
|
403
|
+
`${ep.method}-${ep.path}`
|
|
404
|
+
)) }) })
|
|
405
|
+
] });
|
|
406
|
+
}
|
|
407
|
+
chunkWGEGR3DF_cjs.__name(EndpointList, "EndpointList");
|
|
408
|
+
function ParamFields({ label, params }) {
|
|
409
|
+
const { state, setParameters } = chunkWM4RT5KX_cjs.usePlaygroundContext();
|
|
410
|
+
function handleChange(name, value) {
|
|
411
|
+
setParameters({ ...state.parameters, [name]: value });
|
|
412
|
+
}
|
|
413
|
+
chunkWGEGR3DF_cjs.__name(handleChange, "handleChange");
|
|
414
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
415
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: label }),
|
|
416
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: params.map((p) => {
|
|
417
|
+
const value = state.parameters[p.name] ?? "";
|
|
418
|
+
const placeholder = p.description || p.name;
|
|
419
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
420
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
421
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-[11px] text-foreground/80", children: p.name }),
|
|
422
|
+
p.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[9px] text-destructive font-bold leading-none", children: "*" }),
|
|
423
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-[10px] text-muted-foreground/50", children: p.type })
|
|
424
|
+
] }),
|
|
425
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
426
|
+
components.Input,
|
|
427
|
+
{
|
|
428
|
+
value,
|
|
429
|
+
onChange: (e) => handleChange(p.name, e.target.value),
|
|
430
|
+
placeholder,
|
|
431
|
+
className: "h-8 text-xs font-mono"
|
|
432
|
+
}
|
|
433
|
+
)
|
|
434
|
+
] }, p.name);
|
|
435
|
+
}) })
|
|
436
|
+
] });
|
|
437
|
+
}
|
|
438
|
+
chunkWGEGR3DF_cjs.__name(ParamFields, "ParamFields");
|
|
439
|
+
function RequestPanel() {
|
|
440
|
+
const { state, apiKeys, setRequestBody, setRequestHeaders, setManualApiToken, sendRequest } = chunkWM4RT5KX_cjs.usePlaygroundContext();
|
|
441
|
+
const ep = state.selectedEndpoint;
|
|
442
|
+
const isJsonValid = state.requestBody ? chunkWM4RT5KX_cjs.isValidJson(state.requestBody) : true;
|
|
443
|
+
const curlCommand = React4.useMemo(() => {
|
|
444
|
+
if (!state.requestUrl) return "";
|
|
445
|
+
const apiKey = state.selectedApiKey ? chunkWM4RT5KX_cjs.findApiKeyById(apiKeys, state.selectedApiKey) : null;
|
|
446
|
+
const hdrs = chunkWM4RT5KX_cjs.parseRequestHeaders(state.requestHeaders);
|
|
447
|
+
if (apiKey) hdrs["X-API-Key"] = apiKey.id;
|
|
448
|
+
let cmd = `curl -X ${state.requestMethod} "${state.requestUrl}"`;
|
|
449
|
+
Object.entries(hdrs).forEach(([k, v]) => {
|
|
450
|
+
cmd += ` \\
|
|
451
|
+
-H "${k}: ${v}"`;
|
|
452
|
+
});
|
|
453
|
+
if (state.requestBody && state.requestMethod !== "GET" && isJsonValid) {
|
|
454
|
+
cmd += ` \\
|
|
455
|
+
-d '${state.requestBody}'`;
|
|
456
|
+
}
|
|
457
|
+
return cmd;
|
|
458
|
+
}, [state, apiKeys, isJsonValid]);
|
|
459
|
+
const pathParams = React4.useMemo(
|
|
460
|
+
() => ep?.parameters?.filter((p) => ep.path.includes(`{${p.name}}`)) ?? [],
|
|
461
|
+
[ep]
|
|
462
|
+
);
|
|
463
|
+
const queryParams = React4.useMemo(
|
|
464
|
+
() => ep?.parameters?.filter((p) => !ep.path.includes(`{${p.name}}`)) ?? [],
|
|
465
|
+
[ep]
|
|
466
|
+
);
|
|
467
|
+
const isSendDisabled = state.loading || !state.requestUrl || !isJsonValid;
|
|
468
|
+
const displayUrl = state.requestUrl || ep?.path || "";
|
|
469
|
+
const hasBody = ep?.method !== "GET";
|
|
470
|
+
const bodyType = ep?.requestBody?.type ?? "";
|
|
471
|
+
const hasPathParams = pathParams.length > 0;
|
|
472
|
+
const hasQueryParams = queryParams.length > 0;
|
|
473
|
+
const hasCurl = Boolean(curlCommand);
|
|
474
|
+
const epPath = ep ? relativePath(ep.path) : "";
|
|
475
|
+
const urlChanged = displayUrl !== epPath;
|
|
476
|
+
if (!ep) {
|
|
477
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { icon: lucideReact.Send, text: "Select an endpoint to build a request" });
|
|
478
|
+
}
|
|
479
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
480
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 border-b px-4 py-3 bg-muted/20 space-y-1.5", children: [
|
|
481
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
482
|
+
/* @__PURE__ */ jsxRuntime.jsx(MethodBadge, { method: ep.method }),
|
|
483
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-xs text-foreground/70 truncate min-w-0 flex-1", children: epPath }),
|
|
484
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
485
|
+
components.Button,
|
|
486
|
+
{
|
|
487
|
+
onClick: sendRequest,
|
|
488
|
+
disabled: isSendDisabled,
|
|
489
|
+
size: "sm",
|
|
490
|
+
className: "shrink-0 gap-1.5 h-7 text-xs px-3",
|
|
491
|
+
children: state.loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
492
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3 w-3 animate-spin" }),
|
|
493
|
+
" Sending\u2026"
|
|
494
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
495
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "h-3 w-3" }),
|
|
496
|
+
" Send"
|
|
497
|
+
] })
|
|
498
|
+
}
|
|
499
|
+
)
|
|
500
|
+
] }),
|
|
501
|
+
urlChanged && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-mono text-[10px] text-muted-foreground/50 break-all leading-snug pl-0.5", children: displayUrl })
|
|
502
|
+
] }),
|
|
503
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ScrollArea, { className: "px-4 py-3 space-y-3", children: [
|
|
504
|
+
hasPathParams && /* @__PURE__ */ jsxRuntime.jsx(ParamFields, { label: "Path Parameters", params: pathParams }),
|
|
505
|
+
hasQueryParams && /* @__PURE__ */ jsxRuntime.jsx(ParamFields, { label: "Query Parameters", params: queryParams }),
|
|
506
|
+
hasBody && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
|
|
507
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
508
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2", children: [
|
|
509
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: "Body" }),
|
|
510
|
+
bodyType && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground/40 font-mono", children: bodyType })
|
|
511
|
+
] }),
|
|
512
|
+
isJsonValid && state.requestBody && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
513
|
+
"button",
|
|
514
|
+
{
|
|
515
|
+
type: "button",
|
|
516
|
+
onClick: () => {
|
|
517
|
+
try {
|
|
518
|
+
setRequestBody(JSON.stringify(JSON.parse(state.requestBody), null, 2));
|
|
519
|
+
} catch {
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
className: "inline-flex items-center gap-1 text-[10px] text-muted-foreground hover:text-foreground transition-colors",
|
|
523
|
+
children: [
|
|
524
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "h-2.5 w-2.5" }),
|
|
525
|
+
"Format"
|
|
526
|
+
]
|
|
527
|
+
}
|
|
528
|
+
)
|
|
529
|
+
] }),
|
|
530
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
531
|
+
components.Textarea,
|
|
532
|
+
{
|
|
533
|
+
placeholder: '{\n "key": "value"\n}',
|
|
534
|
+
value: state.requestBody,
|
|
535
|
+
onChange: (e) => setRequestBody(e.target.value),
|
|
536
|
+
className: lib.cn(
|
|
537
|
+
"font-mono text-[11px] min-h-[90px] resize-y",
|
|
538
|
+
!isJsonValid && "border-destructive focus-visible:ring-destructive/30"
|
|
539
|
+
),
|
|
540
|
+
rows: 4
|
|
541
|
+
}
|
|
542
|
+
),
|
|
543
|
+
!isJsonValid && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] text-destructive", children: "Invalid JSON" })
|
|
544
|
+
] }),
|
|
545
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
546
|
+
CollapsibleSection,
|
|
547
|
+
{
|
|
548
|
+
label: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
549
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Key, { className: "h-2.5 w-2.5" }),
|
|
550
|
+
"Auth & Headers"
|
|
551
|
+
] }),
|
|
552
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 pt-2", children: [
|
|
553
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
|
|
554
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: "Bearer Token" }),
|
|
555
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
556
|
+
components.Input,
|
|
557
|
+
{
|
|
558
|
+
type: "password",
|
|
559
|
+
placeholder: "Leave empty to use JWT from localStorage",
|
|
560
|
+
value: state.manualApiToken,
|
|
561
|
+
onChange: (e) => setManualApiToken(e.target.value),
|
|
562
|
+
className: "font-mono text-xs h-8"
|
|
563
|
+
}
|
|
564
|
+
)
|
|
565
|
+
] }),
|
|
566
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
|
|
567
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: "Headers" }),
|
|
568
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
569
|
+
components.Textarea,
|
|
570
|
+
{
|
|
571
|
+
value: state.requestHeaders,
|
|
572
|
+
onChange: (e) => setRequestHeaders(e.target.value),
|
|
573
|
+
className: "font-mono text-[11px] min-h-[60px] resize-y",
|
|
574
|
+
rows: 3
|
|
575
|
+
}
|
|
576
|
+
)
|
|
577
|
+
] })
|
|
578
|
+
] })
|
|
579
|
+
}
|
|
580
|
+
),
|
|
581
|
+
hasCurl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
582
|
+
CollapsibleSection,
|
|
583
|
+
{
|
|
584
|
+
label: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
585
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Terminal, { className: "h-2.5 w-2.5" }),
|
|
586
|
+
"cURL"
|
|
587
|
+
] }),
|
|
588
|
+
action: /* @__PURE__ */ jsxRuntime.jsx(components.CopyButton, { value: curlCommand, variant: "ghost", size: "sm", className: "h-5 px-2 text-[10px] text-muted-foreground", children: "Copy" }),
|
|
589
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md overflow-hidden mt-2", children: /* @__PURE__ */ jsxRuntime.jsx(chunkWM4RT5KX_cjs.PrettyCode_default, { data: curlCommand, language: "bash", isCompact: true }) })
|
|
590
|
+
}
|
|
591
|
+
),
|
|
592
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1" })
|
|
593
|
+
] }),
|
|
594
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 border-t px-4 py-3 bg-background/95 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsx(components.Button, { onClick: sendRequest, disabled: isSendDisabled, size: "sm", className: "w-full gap-2 h-9", children: state.loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
595
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
|
|
596
|
+
" Sending\u2026"
|
|
597
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
598
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "h-3.5 w-3.5" }),
|
|
599
|
+
" Send Request"
|
|
600
|
+
] }) }) })
|
|
601
|
+
] });
|
|
602
|
+
}
|
|
603
|
+
chunkWGEGR3DF_cjs.__name(RequestPanel, "RequestPanel");
|
|
604
|
+
var JSON_TREE_CONFIG = {
|
|
605
|
+
maxAutoExpandDepth: 2,
|
|
606
|
+
maxAutoExpandArrayItems: 10,
|
|
607
|
+
maxAutoExpandObjectKeys: 5,
|
|
608
|
+
maxStringLength: 200,
|
|
609
|
+
collectionLimit: 50,
|
|
610
|
+
showCollectionInfo: true,
|
|
611
|
+
showExpandControls: true,
|
|
612
|
+
showActionButtons: false,
|
|
613
|
+
preserveKeyOrder: true,
|
|
614
|
+
className: "border-0 rounded-none"
|
|
615
|
+
};
|
|
616
|
+
function ResponsePanel() {
|
|
617
|
+
const { state } = chunkWM4RT5KX_cjs.usePlaygroundContext();
|
|
618
|
+
const { response, loading, selectedEndpoint } = state;
|
|
619
|
+
const { treeData, rawText } = React4.useMemo(() => {
|
|
620
|
+
const d = response?.data;
|
|
621
|
+
if (d == null) return { treeData: null, rawText: "" };
|
|
622
|
+
if (typeof d === "string") {
|
|
623
|
+
try {
|
|
624
|
+
return { treeData: JSON.parse(d), rawText: d };
|
|
625
|
+
} catch {
|
|
626
|
+
return { treeData: null, rawText: d };
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return { treeData: d, rawText: JSON.stringify(d, null, 2) };
|
|
630
|
+
}, [response?.data]);
|
|
631
|
+
const sizeKb = rawText ? `${(rawText.length / 1024).toFixed(1)} KB` : "";
|
|
632
|
+
const duration = response?.duration != null ? `${response.duration}ms` : "";
|
|
633
|
+
const hasError = Boolean(response?.error);
|
|
634
|
+
const hasStatus = response?.status != null;
|
|
635
|
+
const hasCopy = Boolean(rawText);
|
|
636
|
+
if (loading) {
|
|
637
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center h-full gap-2", children: [
|
|
638
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }),
|
|
639
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "Sending\u2026" })
|
|
640
|
+
] });
|
|
641
|
+
}
|
|
642
|
+
if (!selectedEndpoint) return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { icon: lucideReact.Terminal, text: "Response will appear here" });
|
|
643
|
+
if (!response) return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { icon: lucideReact.Send, text: 'Press "Send Request" to see the response' });
|
|
644
|
+
if (hasError && !hasStatus) {
|
|
645
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
646
|
+
EmptyState,
|
|
647
|
+
{
|
|
648
|
+
icon: lucideReact.WifiOff,
|
|
649
|
+
text: response.error,
|
|
650
|
+
className: "text-destructive [&_svg]:text-destructive"
|
|
651
|
+
}
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
655
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 border-b px-4 py-2 flex items-center justify-between gap-3 bg-muted/20", children: [
|
|
656
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
657
|
+
hasStatus && /* @__PURE__ */ jsxRuntime.jsx(StatusBadge, { status: response.status }),
|
|
658
|
+
response.statusText && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground truncate", children: response.statusText }),
|
|
659
|
+
sizeKb && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground/50 tabular-nums shrink-0", children: sizeKb }),
|
|
660
|
+
duration && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground/50 tabular-nums shrink-0", children: duration })
|
|
661
|
+
] }),
|
|
662
|
+
hasCopy && /* @__PURE__ */ jsxRuntime.jsx(components.CopyButton, { value: rawText, variant: "ghost", size: "sm", className: "h-6 px-2 text-[10px] text-muted-foreground shrink-0", children: "Copy" })
|
|
663
|
+
] }),
|
|
664
|
+
hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 mx-4 mt-3 rounded border border-destructive/20 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: response.error }) }),
|
|
665
|
+
/* @__PURE__ */ jsxRuntime.jsx(ScrollArea, { children: treeData != null ? /* @__PURE__ */ jsxRuntime.jsx(chunk33AMWFBZ_cjs.JsonTree_default, { title: "Response Body", data: treeData, config: JSON_TREE_CONFIG }) : rawText ? /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "p-4 text-[11px] font-mono text-foreground/70 whitespace-pre-wrap break-all leading-relaxed", children: rawText }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-10 text-center text-xs text-muted-foreground", children: "Empty response body" }) })
|
|
666
|
+
] });
|
|
667
|
+
}
|
|
668
|
+
chunkWGEGR3DF_cjs.__name(ResponsePanel, "ResponsePanel");
|
|
669
|
+
var MOBILE_TABS = [
|
|
670
|
+
{ id: "endpoints", label: "Endpoints" },
|
|
671
|
+
{ id: "request", label: "Request" },
|
|
672
|
+
{ id: "response", label: "Response" }
|
|
673
|
+
];
|
|
674
|
+
function MobileView() {
|
|
675
|
+
const { state } = chunkWM4RT5KX_cjs.usePlaygroundContext();
|
|
676
|
+
const [tab, setTab] = React4__default.default.useState("endpoints");
|
|
677
|
+
React4__default.default.useEffect(() => {
|
|
678
|
+
if (state.selectedEndpoint) setTab("request");
|
|
679
|
+
}, [state.selectedEndpoint?.path, state.selectedEndpoint?.method]);
|
|
680
|
+
React4__default.default.useEffect(() => {
|
|
681
|
+
if (state.response && !state.loading) setTab("response");
|
|
682
|
+
}, [state.response, state.loading]);
|
|
683
|
+
const hasResponse = Boolean(state.response) && !state.loading;
|
|
684
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Panel, { className: "h-full", children: [
|
|
685
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 flex border-b", children: MOBILE_TABS.map((t) => {
|
|
686
|
+
const isActive = tab === t.id;
|
|
687
|
+
const showDot = t.id === "response" && hasResponse;
|
|
688
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
689
|
+
"button",
|
|
690
|
+
{
|
|
691
|
+
onClick: () => setTab(t.id),
|
|
692
|
+
className: lib.cn(
|
|
693
|
+
"flex-1 py-2.5 text-xs font-medium transition-colors border-b-2 -mb-px relative",
|
|
694
|
+
isActive ? "border-primary text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"
|
|
695
|
+
),
|
|
696
|
+
children: [
|
|
697
|
+
t.label,
|
|
698
|
+
showDot && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute top-2 right-[calc(50%-16px)] h-1.5 w-1.5 rounded-full bg-primary" })
|
|
699
|
+
]
|
|
700
|
+
},
|
|
701
|
+
t.id
|
|
702
|
+
);
|
|
703
|
+
}) }),
|
|
704
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Panel, { className: "flex-1", children: [
|
|
705
|
+
tab === "endpoints" && /* @__PURE__ */ jsxRuntime.jsx(EndpointList, {}),
|
|
706
|
+
tab === "request" && /* @__PURE__ */ jsxRuntime.jsx(RequestPanel, {}),
|
|
707
|
+
tab === "response" && /* @__PURE__ */ jsxRuntime.jsx(ResponsePanel, {})
|
|
708
|
+
] })
|
|
709
|
+
] });
|
|
710
|
+
}
|
|
711
|
+
chunkWGEGR3DF_cjs.__name(MobileView, "MobileView");
|
|
712
|
+
function DesktopView() {
|
|
713
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-[260px_1fr_1fr] divide-x h-full min-h-0 overflow-hidden", children: [
|
|
714
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Panel, { children: [
|
|
715
|
+
/* @__PURE__ */ jsxRuntime.jsx(PanelHeader, { title: "Endpoints" }),
|
|
716
|
+
/* @__PURE__ */ jsxRuntime.jsx(EndpointList, {})
|
|
717
|
+
] }),
|
|
718
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Panel, { children: [
|
|
719
|
+
/* @__PURE__ */ jsxRuntime.jsx(PanelHeader, { title: "Request" }),
|
|
720
|
+
/* @__PURE__ */ jsxRuntime.jsx(RequestPanel, {})
|
|
721
|
+
] }),
|
|
722
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Panel, { children: [
|
|
723
|
+
/* @__PURE__ */ jsxRuntime.jsx(PanelHeader, { title: "Response" }),
|
|
724
|
+
/* @__PURE__ */ jsxRuntime.jsx(ResponsePanel, {})
|
|
725
|
+
] })
|
|
726
|
+
] });
|
|
727
|
+
}
|
|
728
|
+
chunkWGEGR3DF_cjs.__name(DesktopView, "DesktopView");
|
|
729
|
+
var PlaygroundLayout = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(() => {
|
|
730
|
+
const { isMobile } = useMobile();
|
|
731
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
732
|
+
"div",
|
|
733
|
+
{
|
|
734
|
+
className: "flex flex-col overflow-hidden",
|
|
735
|
+
style: { height: "calc(100dvh - var(--navbar-height, 64px))" },
|
|
736
|
+
children: isMobile ? /* @__PURE__ */ jsxRuntime.jsx(MobileView, {}) : /* @__PURE__ */ jsxRuntime.jsx(DesktopView, {})
|
|
737
|
+
}
|
|
738
|
+
);
|
|
739
|
+
}, "PlaygroundLayout");
|
|
740
|
+
|
|
741
|
+
exports.PlaygroundLayout = PlaygroundLayout;
|
|
742
|
+
//# sourceMappingURL=PlaygroundLayout-ZO2LO7M5.cjs.map
|
|
743
|
+
//# sourceMappingURL=PlaygroundLayout-ZO2LO7M5.cjs.map
|