@djangocfg/ui-tools 2.1.287 → 2.1.290
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/README.md +14 -3
- package/dist/DocsLayout-IKH7BLSU.cjs +3464 -0
- package/dist/DocsLayout-IKH7BLSU.cjs.map +1 -0
- package/dist/DocsLayout-JPXFUKAR.mjs +3457 -0
- package/dist/DocsLayout-JPXFUKAR.mjs.map +1 -0
- package/dist/{PrettyCode.client-5GABIN2I.cjs → PrettyCode.client-RPDIE5CH.cjs} +104 -3
- package/dist/PrettyCode.client-RPDIE5CH.cjs.map +1 -0
- package/dist/{PrettyCode.client-IZTXXYHG.mjs → PrettyCode.client-SPMTQEG4.mjs} +106 -5
- package/dist/PrettyCode.client-SPMTQEG4.mjs.map +1 -0
- package/dist/{chunk-IULI4XII.cjs → chunk-5Q4UMSWB.cjs} +355 -9
- package/dist/chunk-5Q4UMSWB.cjs.map +1 -0
- package/dist/{chunk-VZGQC3NG.mjs → chunk-EFWOJPA6.mjs} +349 -9
- package/dist/chunk-EFWOJPA6.mjs.map +1 -0
- package/dist/index.cjs +10 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.mjs +5 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +21 -14
- package/src/components/markdown/MarkdownMessage.tsx +46 -0
- package/src/tools/OpenapiViewer/OpenapiViewer.story.tsx +93 -157
- package/src/tools/OpenapiViewer/README.md +114 -6
- package/src/tools/OpenapiViewer/components/DocsLayout/ApiIntroSection.tsx +20 -6
- package/src/tools/OpenapiViewer/components/DocsLayout/DocsView.tsx +331 -53
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/CodeSamples/LanguageTabs.tsx +36 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/CodeSamples/index.tsx +56 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/CodeSamples/useCodeSnippet.ts +77 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +146 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/MethodBadge.tsx +6 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/PathDisplay.tsx +26 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/index.tsx +87 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Parameters/ParamGroup.tsx +30 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Parameters/ParamRow.tsx +36 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Parameters/index.tsx +22 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/RequestBody/index.tsx +33 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Responses/ResponseBody.tsx +76 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Responses/ResponseRow.tsx +80 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Responses/StatusTag.tsx +32 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Responses/index.tsx +21 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/FieldRow.tsx +106 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/buildTree.ts +127 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/index.tsx +31 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/types.ts +28 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Section/SectionHeader.tsx +87 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Section/defaults.ts +27 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Section/index.tsx +45 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/context.tsx +56 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/hooks/useSectionHash.ts +63 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/index.tsx +96 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/store/index.ts +133 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/store/selectors.ts +40 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/types.ts +17 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/SchemaCopyMenu.tsx +40 -11
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/BrandHeader.tsx +48 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/CategoryBlock.tsx +33 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/EndpointRow.tsx +73 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/MethodChips.tsx +43 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/SchemaSection.tsx +27 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/SearchInput.tsx +45 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/SidebarBody.tsx +50 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/Toolbar.tsx +64 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/buildVM.ts +126 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/index.tsx +112 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/types.ts +42 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/useDebouncedValue.ts +14 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/SlideInPlayground.tsx +10 -7
- package/src/tools/OpenapiViewer/components/DocsLayout/TryItSheet.tsx +9 -6
- package/src/tools/OpenapiViewer/components/DocsLayout/anchor.ts +19 -2
- package/src/tools/OpenapiViewer/components/DocsLayout/grouping.ts +38 -21
- package/src/tools/OpenapiViewer/components/DocsLayout/index.tsx +168 -50
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/PrettyView.tsx +55 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/PreviewView.tsx +115 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/RawView.tsx +24 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/StatusBar.tsx +63 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/ViewTabs.tsx +45 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/detectContent.ts +97 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/index.tsx +93 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/types.ts +26 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/useResponseView.ts +62 -0
- package/src/tools/OpenapiViewer/hooks/index.ts +3 -1
- package/src/tools/OpenapiViewer/hooks/useDocsUrlSync.ts +119 -0
- package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +164 -74
- package/src/tools/OpenapiViewer/types.ts +46 -1
- package/src/tools/OpenapiViewer/utils/codeSamples.ts +287 -0
- package/src/tools/OpenapiViewer/utils/index.ts +3 -0
- package/src/tools/OpenapiViewer/utils/operationToHar.ts +119 -0
- package/src/tools/OpenapiViewer/utils/sampler.ts +72 -0
- package/src/tools/OpenapiViewer/utils/scrollParent.ts +68 -0
- package/src/tools/PrettyCode/PrettyCode.client.tsx +88 -1
- package/src/tools/PrettyCode/PrettyCode.story.tsx +114 -361
- package/src/tools/PrettyCode/index.tsx +13 -0
- package/src/tools/PrettyCode/lazy.tsx +5 -0
- package/src/tools/PrettyCode/registerPrismLanguages.ts +111 -0
- package/dist/DocsLayout-BCVU6TTX.cjs +0 -2027
- package/dist/DocsLayout-BCVU6TTX.cjs.map +0 -1
- package/dist/DocsLayout-ERETJLLV.mjs +0 -2020
- package/dist/DocsLayout-ERETJLLV.mjs.map +0 -1
- package/dist/PrettyCode.client-5GABIN2I.cjs.map +0 -1
- package/dist/PrettyCode.client-IZTXXYHG.mjs.map +0 -1
- package/dist/chunk-IULI4XII.cjs.map +0 -1
- package/dist/chunk-VZGQC3NG.mjs.map +0 -1
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc.tsx +0 -268
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar.tsx +0 -211
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel.tsx +0 -127
|
@@ -1,2020 +0,0 @@
|
|
|
1
|
-
import { deduplicateEndpoints, dereferenceSchema, resolveBaseUrl, usePlaygroundContext, toMarkdown, toCompactJson, toRawJson, formatBytes, MarkdownMessage, endpointToMarkdown, relativePath, isValidJson, resolveAbsolute, findApiKeyById, parseRequestHeaders, PrettyCode_default, UrlBuilder, joinUrl } from './chunk-VZGQC3NG.mjs';
|
|
2
|
-
import { JsonTree_default } from './chunk-LFWQ36LJ.mjs';
|
|
3
|
-
import './chunk-SSUOENAZ.mjs';
|
|
4
|
-
import { __name } from './chunk-CGILA3WO.mjs';
|
|
5
|
-
import React6, { useRef, useMemo, useCallback, useEffect, useState } from 'react';
|
|
6
|
-
import { Combobox, Input, Tooltip, TooltipTrigger, TooltipContent, DropdownMenu, DropdownMenuTrigger, Button, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuItem, CopyButton, Switch, Textarea, SidePanel, ResponsiveSheet, ResponsiveSheetContent, ResponsiveSheetHeader, ResponsiveSheetTitle, Skeleton, TooltipProvider } from '@djangocfg/ui-core/components';
|
|
7
|
-
import { useMediaQuery } from '@djangocfg/ui-core/hooks';
|
|
8
|
-
import consola from 'consola';
|
|
9
|
-
import { ChevronRight, Search, Sparkles, ChevronDown, Check, Link2, Play, Minus, Plus, RotateCcw, Send, Key, Terminal, Loader2, WifiOff, AlertCircle } from 'lucide-react';
|
|
10
|
-
import { cn } from '@djangocfg/ui-core/lib';
|
|
11
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
12
|
-
|
|
13
|
-
function exampleFromSchema(schema, depth = 0) {
|
|
14
|
-
if (!schema || depth > 8) return null;
|
|
15
|
-
if (schema.example !== void 0) return schema.example;
|
|
16
|
-
if (schema.default !== void 0) return schema.default;
|
|
17
|
-
if (Array.isArray(schema.enum) && schema.enum.length > 0) return schema.enum[0];
|
|
18
|
-
switch (schema.type) {
|
|
19
|
-
case "object": {
|
|
20
|
-
const out = {};
|
|
21
|
-
const props = schema.properties ?? {};
|
|
22
|
-
for (const [k, v] of Object.entries(props)) {
|
|
23
|
-
out[k] = exampleFromSchema(v, depth + 1);
|
|
24
|
-
}
|
|
25
|
-
return out;
|
|
26
|
-
}
|
|
27
|
-
case "array":
|
|
28
|
-
return [exampleFromSchema(schema.items, depth + 1)];
|
|
29
|
-
case "integer":
|
|
30
|
-
case "number":
|
|
31
|
-
return 0;
|
|
32
|
-
case "boolean":
|
|
33
|
-
return false;
|
|
34
|
-
case "string":
|
|
35
|
-
if (schema.format === "date-time") return (/* @__PURE__ */ new Date()).toISOString();
|
|
36
|
-
if (schema.format === "date") return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
37
|
-
if (schema.format === "email") return "user@example.com";
|
|
38
|
-
if (schema.format === "uri" || schema.format === "url") return "https://example.com";
|
|
39
|
-
if (schema.format === "uuid") return "00000000-0000-0000-0000-000000000000";
|
|
40
|
-
return "";
|
|
41
|
-
default:
|
|
42
|
-
if (schema.properties) {
|
|
43
|
-
const out = {};
|
|
44
|
-
for (const [k, v] of Object.entries(schema.properties)) {
|
|
45
|
-
out[k] = exampleFromSchema(v, depth + 1);
|
|
46
|
-
}
|
|
47
|
-
return out;
|
|
48
|
-
}
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
__name(exampleFromSchema, "exampleFromSchema");
|
|
53
|
-
var HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
|
|
54
|
-
var extractEndpoints = /* @__PURE__ */ __name((schema, baseUrl) => {
|
|
55
|
-
const endpoints = [];
|
|
56
|
-
if (!schema.paths) return [];
|
|
57
|
-
for (const [path, methods] of Object.entries(schema.paths)) {
|
|
58
|
-
for (const method of HTTP_METHODS) {
|
|
59
|
-
const op = methods[method];
|
|
60
|
-
if (!op) continue;
|
|
61
|
-
const methodUpper = method.toUpperCase();
|
|
62
|
-
const summary = (op.summary || "").trim();
|
|
63
|
-
const description = op.description || summary || `${methodUpper} ${path}`;
|
|
64
|
-
const category = op.tags?.[0] || "Other";
|
|
65
|
-
const parameters = [];
|
|
66
|
-
const allParams = [...methods.parameters || [], ...op.parameters || []];
|
|
67
|
-
for (const param of allParams) {
|
|
68
|
-
parameters.push({
|
|
69
|
-
name: param.name,
|
|
70
|
-
type: param.schema?.type || "string",
|
|
71
|
-
required: param.required || false,
|
|
72
|
-
description: param.description
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
const responses = [];
|
|
76
|
-
if (op.responses) {
|
|
77
|
-
for (const [code, response] of Object.entries(op.responses)) {
|
|
78
|
-
responses.push({
|
|
79
|
-
code,
|
|
80
|
-
description: response.description || `Response ${code}`
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
let requestBody;
|
|
85
|
-
if (op.requestBody) {
|
|
86
|
-
const content = op.requestBody.content;
|
|
87
|
-
const mediaType = content?.["application/json"] || content?.[Object.keys(content || {})[0]];
|
|
88
|
-
const rawSchema = mediaType?.schema;
|
|
89
|
-
requestBody = {
|
|
90
|
-
type: rawSchema?.type || "object",
|
|
91
|
-
description: op.requestBody.description,
|
|
92
|
-
schema: rawSchema,
|
|
93
|
-
example: rawSchema ? JSON.stringify(exampleFromSchema(rawSchema), null, 2) : void 0
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
const endpoint = {
|
|
97
|
-
name: path.split("/").pop() || path,
|
|
98
|
-
method: methodUpper,
|
|
99
|
-
path: baseUrl ? joinUrl(baseUrl, path) : path,
|
|
100
|
-
summary,
|
|
101
|
-
description,
|
|
102
|
-
category,
|
|
103
|
-
parameters: parameters.length > 0 ? parameters : void 0,
|
|
104
|
-
requestBody,
|
|
105
|
-
responses: responses.length > 0 ? responses : void 0
|
|
106
|
-
};
|
|
107
|
-
endpoints.push(endpoint);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return endpoints;
|
|
111
|
-
}, "extractEndpoints");
|
|
112
|
-
var getCategories = /* @__PURE__ */ __name((endpoints) => {
|
|
113
|
-
const categories = /* @__PURE__ */ new Set();
|
|
114
|
-
endpoints.forEach((endpoint) => categories.add(endpoint.category));
|
|
115
|
-
return Array.from(categories).sort();
|
|
116
|
-
}, "getCategories");
|
|
117
|
-
var fetchSchema = /* @__PURE__ */ __name(async (url) => {
|
|
118
|
-
const response = await fetch(url, {
|
|
119
|
-
headers: {
|
|
120
|
-
"Accept": "application/json"
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
if (!response.ok) {
|
|
124
|
-
throw new Error(`Failed to fetch schema: ${response.statusText}`);
|
|
125
|
-
}
|
|
126
|
-
return response.json();
|
|
127
|
-
}, "fetchSchema");
|
|
128
|
-
function useOpenApiSchema({
|
|
129
|
-
schemas,
|
|
130
|
-
defaultSchemaId,
|
|
131
|
-
baseUrl: configBaseUrl
|
|
132
|
-
}) {
|
|
133
|
-
const [loading, setLoading] = useState(true);
|
|
134
|
-
const [error, setError] = useState(null);
|
|
135
|
-
const [currentSchemaId, setCurrentSchemaId] = useState(
|
|
136
|
-
defaultSchemaId || schemas[0]?.id
|
|
137
|
-
);
|
|
138
|
-
const [loadedSchemas, setLoadedSchemas] = useState(
|
|
139
|
-
/* @__PURE__ */ new Map()
|
|
140
|
-
);
|
|
141
|
-
const currentSchema = useMemo(
|
|
142
|
-
() => schemas.find((s) => s.id === currentSchemaId) || null,
|
|
143
|
-
[schemas, currentSchemaId]
|
|
144
|
-
);
|
|
145
|
-
const currentOpenApiSchema = useMemo(
|
|
146
|
-
() => currentSchemaId ? loadedSchemas.get(currentSchemaId) : null,
|
|
147
|
-
[loadedSchemas, currentSchemaId]
|
|
148
|
-
);
|
|
149
|
-
const dereferencedSchema = useMemo(
|
|
150
|
-
() => currentOpenApiSchema ? dereferenceSchema(currentOpenApiSchema) : null,
|
|
151
|
-
[currentOpenApiSchema]
|
|
152
|
-
);
|
|
153
|
-
const resolvedBaseUrl = useMemo(
|
|
154
|
-
() => resolveBaseUrl({
|
|
155
|
-
schemaSource: currentSchema?.baseUrl,
|
|
156
|
-
config: configBaseUrl,
|
|
157
|
-
fromServers: currentOpenApiSchema?.servers?.[0]?.url
|
|
158
|
-
}),
|
|
159
|
-
[currentSchema?.baseUrl, configBaseUrl, currentOpenApiSchema]
|
|
160
|
-
);
|
|
161
|
-
const endpoints = useMemo(
|
|
162
|
-
() => dereferencedSchema ? extractEndpoints(dereferencedSchema, resolvedBaseUrl) : [],
|
|
163
|
-
[dereferencedSchema, resolvedBaseUrl]
|
|
164
|
-
);
|
|
165
|
-
const categories = useMemo(() => getCategories(endpoints), [endpoints]);
|
|
166
|
-
const schemaInfo = useMemo(() => {
|
|
167
|
-
if (!currentOpenApiSchema?.info) return null;
|
|
168
|
-
const { title, version, description } = currentOpenApiSchema.info;
|
|
169
|
-
return {
|
|
170
|
-
title,
|
|
171
|
-
version,
|
|
172
|
-
description,
|
|
173
|
-
servers: currentOpenApiSchema.servers
|
|
174
|
-
};
|
|
175
|
-
}, [currentOpenApiSchema]);
|
|
176
|
-
useEffect(() => {
|
|
177
|
-
if (!currentSchema) return;
|
|
178
|
-
if (loadedSchemas.has(currentSchema.id)) {
|
|
179
|
-
setLoading(false);
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
setLoading(true);
|
|
183
|
-
setError(null);
|
|
184
|
-
fetchSchema(currentSchema.url).then((schema) => {
|
|
185
|
-
setLoadedSchemas((prev) => new Map(prev).set(currentSchema.id, schema));
|
|
186
|
-
consola.success(`Schema loaded: ${currentSchema.name}`);
|
|
187
|
-
setLoading(false);
|
|
188
|
-
}).catch((err) => {
|
|
189
|
-
consola.error(`Error loading schema from ${currentSchema.url}:`, err);
|
|
190
|
-
setError(err instanceof Error ? err.message : "Failed to load schema");
|
|
191
|
-
setLoading(false);
|
|
192
|
-
});
|
|
193
|
-
}, [currentSchema, loadedSchemas]);
|
|
194
|
-
const setCurrentSchema = useCallback((schemaId) => {
|
|
195
|
-
setCurrentSchemaId(schemaId);
|
|
196
|
-
}, []);
|
|
197
|
-
const refresh = useCallback(() => {
|
|
198
|
-
if (!currentSchema) return;
|
|
199
|
-
setLoading(true);
|
|
200
|
-
setError(null);
|
|
201
|
-
setLoadedSchemas((prev) => {
|
|
202
|
-
const next = new Map(prev);
|
|
203
|
-
next.delete(currentSchema.id);
|
|
204
|
-
return next;
|
|
205
|
-
});
|
|
206
|
-
fetchSchema(currentSchema.url).then((schema) => {
|
|
207
|
-
setLoadedSchemas((prev) => new Map(prev).set(currentSchema.id, schema));
|
|
208
|
-
consola.success(`Schema refreshed: ${currentSchema.name}`);
|
|
209
|
-
setLoading(false);
|
|
210
|
-
}).catch((err) => {
|
|
211
|
-
consola.error(`Error refreshing schema from ${currentSchema.url}:`, err);
|
|
212
|
-
setError(err instanceof Error ? err.message : "Failed to refresh schema");
|
|
213
|
-
setLoading(false);
|
|
214
|
-
});
|
|
215
|
-
}, [currentSchema]);
|
|
216
|
-
return {
|
|
217
|
-
loading,
|
|
218
|
-
error,
|
|
219
|
-
endpoints,
|
|
220
|
-
categories,
|
|
221
|
-
schemas,
|
|
222
|
-
currentSchema,
|
|
223
|
-
schemaInfo,
|
|
224
|
-
rawSchema: currentOpenApiSchema ?? null,
|
|
225
|
-
// Consumers expect ``undefined`` when no base URL was resolved (for
|
|
226
|
-
// conditional ``{ baseUrl?: … }`` plumbing). Turn the empty-string
|
|
227
|
-
// convention from the resolver into undefined at the API boundary.
|
|
228
|
-
resolvedBaseUrl: resolvedBaseUrl || void 0,
|
|
229
|
-
setCurrentSchema,
|
|
230
|
-
refresh
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
__name(useOpenApiSchema, "useOpenApiSchema");
|
|
234
|
-
var EMPTY_DRAFT = { parameters: {}, requestBody: "" };
|
|
235
|
-
function storageKey(schemaId, ep) {
|
|
236
|
-
if (!schemaId || !ep) return null;
|
|
237
|
-
return `openapi-playground:draft:${schemaId}:${ep.method}:${ep.path}`;
|
|
238
|
-
}
|
|
239
|
-
__name(storageKey, "storageKey");
|
|
240
|
-
function readDraft(key) {
|
|
241
|
-
if (!key || typeof window === "undefined") return EMPTY_DRAFT;
|
|
242
|
-
try {
|
|
243
|
-
const raw = window.localStorage.getItem(key);
|
|
244
|
-
if (!raw) return EMPTY_DRAFT;
|
|
245
|
-
const parsed = JSON.parse(raw);
|
|
246
|
-
return {
|
|
247
|
-
parameters: parsed?.parameters ?? {},
|
|
248
|
-
requestBody: typeof parsed?.requestBody === "string" ? parsed.requestBody : ""
|
|
249
|
-
};
|
|
250
|
-
} catch {
|
|
251
|
-
return EMPTY_DRAFT;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
__name(readDraft, "readDraft");
|
|
255
|
-
function writeDraft(key, value) {
|
|
256
|
-
if (!key || typeof window === "undefined") return;
|
|
257
|
-
try {
|
|
258
|
-
if (Object.keys(value.parameters).length === 0 && !value.requestBody) {
|
|
259
|
-
window.localStorage.removeItem(key);
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
window.localStorage.setItem(key, JSON.stringify(value));
|
|
263
|
-
} catch {
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
__name(writeDraft, "writeDraft");
|
|
267
|
-
function useEndpointDraft(schemaId, endpoint) {
|
|
268
|
-
const key = storageKey(schemaId, endpoint);
|
|
269
|
-
const [draft, setDraftSnapshot] = useState(() => readDraft(key));
|
|
270
|
-
const loadedKeyRef = useRef(key);
|
|
271
|
-
useEffect(() => {
|
|
272
|
-
if (loadedKeyRef.current === key) return;
|
|
273
|
-
loadedKeyRef.current = key;
|
|
274
|
-
setDraftSnapshot(readDraft(key));
|
|
275
|
-
}, [key]);
|
|
276
|
-
const keyRef = useRef(key);
|
|
277
|
-
useEffect(() => {
|
|
278
|
-
keyRef.current = key;
|
|
279
|
-
}, [key]);
|
|
280
|
-
const latestRef = useRef(draft);
|
|
281
|
-
useEffect(() => {
|
|
282
|
-
latestRef.current = draft;
|
|
283
|
-
}, [draft]);
|
|
284
|
-
const setParameters = useCallback((params) => {
|
|
285
|
-
const next = {
|
|
286
|
-
parameters: params,
|
|
287
|
-
requestBody: latestRef.current.requestBody
|
|
288
|
-
};
|
|
289
|
-
latestRef.current = next;
|
|
290
|
-
writeDraft(keyRef.current, next);
|
|
291
|
-
}, []);
|
|
292
|
-
const setRequestBody = useCallback((body) => {
|
|
293
|
-
const next = {
|
|
294
|
-
parameters: latestRef.current.parameters,
|
|
295
|
-
requestBody: body
|
|
296
|
-
};
|
|
297
|
-
latestRef.current = next;
|
|
298
|
-
writeDraft(keyRef.current, next);
|
|
299
|
-
}, []);
|
|
300
|
-
const reset = useCallback(() => {
|
|
301
|
-
latestRef.current = EMPTY_DRAFT;
|
|
302
|
-
if (keyRef.current && typeof window !== "undefined") {
|
|
303
|
-
try {
|
|
304
|
-
window.localStorage.removeItem(keyRef.current);
|
|
305
|
-
} catch {
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
setDraftSnapshot(EMPTY_DRAFT);
|
|
309
|
-
}, []);
|
|
310
|
-
return { draft, setParameters, setRequestBody, reset };
|
|
311
|
-
}
|
|
312
|
-
__name(useEndpointDraft, "useEndpointDraft");
|
|
313
|
-
|
|
314
|
-
// src/tools/OpenapiViewer/components/shared/EndpointDraftSync.tsx
|
|
315
|
-
function EndpointDraftSync({ schemaId }) {
|
|
316
|
-
const { state, setParameters, setRequestBody, setActiveSchemaId } = usePlaygroundContext();
|
|
317
|
-
const ep = state.selectedEndpoint;
|
|
318
|
-
useEffect(() => {
|
|
319
|
-
setActiveSchemaId(schemaId);
|
|
320
|
-
}, [schemaId, setActiveSchemaId]);
|
|
321
|
-
const { draft, setParameters: persistParams, setRequestBody: persistBody } = useEndpointDraft(schemaId, ep);
|
|
322
|
-
const lastLoadedKeyRef = useRef(null);
|
|
323
|
-
const lastPersistedParamsRef = useRef("");
|
|
324
|
-
const lastPersistedBodyRef = useRef("");
|
|
325
|
-
const currentKey = ep ? `${ep.method}|${ep.path}` : null;
|
|
326
|
-
useEffect(() => {
|
|
327
|
-
if (!ep || !currentKey) {
|
|
328
|
-
lastLoadedKeyRef.current = null;
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
if (lastLoadedKeyRef.current === currentKey) return;
|
|
332
|
-
lastLoadedKeyRef.current = currentKey;
|
|
333
|
-
const hasStoredParams = draft.parameters && Object.keys(draft.parameters).length > 0;
|
|
334
|
-
const hasStoredBody = typeof draft.requestBody === "string" && draft.requestBody !== "";
|
|
335
|
-
if (hasStoredParams) {
|
|
336
|
-
setParameters(draft.parameters);
|
|
337
|
-
lastPersistedParamsRef.current = JSON.stringify(draft.parameters);
|
|
338
|
-
} else {
|
|
339
|
-
lastPersistedParamsRef.current = JSON.stringify(state.parameters);
|
|
340
|
-
}
|
|
341
|
-
if (hasStoredBody) {
|
|
342
|
-
setRequestBody(draft.requestBody);
|
|
343
|
-
lastPersistedBodyRef.current = draft.requestBody;
|
|
344
|
-
} else {
|
|
345
|
-
lastPersistedBodyRef.current = state.requestBody;
|
|
346
|
-
}
|
|
347
|
-
}, [currentKey]);
|
|
348
|
-
useEffect(() => {
|
|
349
|
-
if (!ep || lastLoadedKeyRef.current !== currentKey) return;
|
|
350
|
-
const serialised = JSON.stringify(state.parameters);
|
|
351
|
-
if (serialised === lastPersistedParamsRef.current) return;
|
|
352
|
-
lastPersistedParamsRef.current = serialised;
|
|
353
|
-
persistParams(state.parameters);
|
|
354
|
-
}, [state.parameters, ep, currentKey, persistParams]);
|
|
355
|
-
useEffect(() => {
|
|
356
|
-
if (!ep || lastLoadedKeyRef.current !== currentKey) return;
|
|
357
|
-
if (state.requestBody === lastPersistedBodyRef.current) return;
|
|
358
|
-
lastPersistedBodyRef.current = state.requestBody;
|
|
359
|
-
persistBody(state.requestBody);
|
|
360
|
-
}, [state.requestBody, ep, currentKey, persistBody]);
|
|
361
|
-
return null;
|
|
362
|
-
}
|
|
363
|
-
__name(EndpointDraftSync, "EndpointDraftSync");
|
|
364
|
-
var METHOD_STYLES = {
|
|
365
|
-
GET: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/25",
|
|
366
|
-
POST: "bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/25",
|
|
367
|
-
PUT: "bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/25",
|
|
368
|
-
PATCH: "bg-orange-500/10 text-orange-600 dark:text-orange-400 border-orange-500/25",
|
|
369
|
-
DELETE: "bg-red-500/10 text-red-600 dark:text-red-400 border-red-500/25"
|
|
370
|
-
};
|
|
371
|
-
var METHOD_FALLBACK = "bg-muted text-muted-foreground border-border";
|
|
372
|
-
function getMethodStyle(method) {
|
|
373
|
-
return METHOD_STYLES[method.toUpperCase()] ?? METHOD_FALLBACK;
|
|
374
|
-
}
|
|
375
|
-
__name(getMethodStyle, "getMethodStyle");
|
|
376
|
-
function getStatusStyle(status) {
|
|
377
|
-
if (status >= 500) return "bg-red-500/10 text-red-500 dark:text-red-400 border-red-500/25";
|
|
378
|
-
if (status >= 400) return "bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/25";
|
|
379
|
-
if (status >= 300) return "bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/25";
|
|
380
|
-
return "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/25";
|
|
381
|
-
}
|
|
382
|
-
__name(getStatusStyle, "getStatusStyle");
|
|
383
|
-
function MethodBadge({ method }) {
|
|
384
|
-
return /* @__PURE__ */ jsx("span", { className: cn(
|
|
385
|
-
"inline-flex shrink-0 items-center rounded border px-1.5 py-px",
|
|
386
|
-
"font-mono text-[10px] font-bold uppercase tracking-wider leading-none",
|
|
387
|
-
getMethodStyle(method)
|
|
388
|
-
), children: method });
|
|
389
|
-
}
|
|
390
|
-
__name(MethodBadge, "MethodBadge");
|
|
391
|
-
function StatusBadge({ status }) {
|
|
392
|
-
return /* @__PURE__ */ jsx("span", { className: cn(
|
|
393
|
-
"inline-flex items-center rounded border px-1.5 py-px",
|
|
394
|
-
"font-mono text-[11px] font-bold leading-none",
|
|
395
|
-
getStatusStyle(status)
|
|
396
|
-
), children: status });
|
|
397
|
-
}
|
|
398
|
-
__name(StatusBadge, "StatusBadge");
|
|
399
|
-
function SectionLabel({ children }) {
|
|
400
|
-
return /* @__PURE__ */ jsx("p", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/60 select-none", children });
|
|
401
|
-
}
|
|
402
|
-
__name(SectionLabel, "SectionLabel");
|
|
403
|
-
function Panel({ children, className }) {
|
|
404
|
-
return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col min-h-0 overflow-hidden", className), children });
|
|
405
|
-
}
|
|
406
|
-
__name(Panel, "Panel");
|
|
407
|
-
function ScrollArea({ children, className }) {
|
|
408
|
-
return /* @__PURE__ */ jsx("div", { className: cn("flex-1 overflow-y-auto min-h-0", className), children });
|
|
409
|
-
}
|
|
410
|
-
__name(ScrollArea, "ScrollArea");
|
|
411
|
-
function EmptyState({
|
|
412
|
-
icon: Icon,
|
|
413
|
-
text,
|
|
414
|
-
className
|
|
415
|
-
}) {
|
|
416
|
-
return /* @__PURE__ */ jsxs(
|
|
417
|
-
"div",
|
|
418
|
-
{
|
|
419
|
-
className: cn(
|
|
420
|
-
"flex flex-col items-center justify-center h-full gap-3 px-6 text-center",
|
|
421
|
-
className
|
|
422
|
-
),
|
|
423
|
-
children: [
|
|
424
|
-
/* @__PURE__ */ jsx(Icon, { className: "h-7 w-7 text-muted-foreground/25" }),
|
|
425
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: text })
|
|
426
|
-
]
|
|
427
|
-
}
|
|
428
|
-
);
|
|
429
|
-
}
|
|
430
|
-
__name(EmptyState, "EmptyState");
|
|
431
|
-
function CollapsibleSection({
|
|
432
|
-
label,
|
|
433
|
-
action,
|
|
434
|
-
children,
|
|
435
|
-
defaultOpen = false
|
|
436
|
-
}) {
|
|
437
|
-
const [open, setOpen] = React6.useState(defaultOpen);
|
|
438
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-0", children: [
|
|
439
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
440
|
-
/* @__PURE__ */ jsxs(
|
|
441
|
-
"button",
|
|
442
|
-
{
|
|
443
|
-
type: "button",
|
|
444
|
-
onClick: () => setOpen((v) => !v),
|
|
445
|
-
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",
|
|
446
|
-
children: [
|
|
447
|
-
/* @__PURE__ */ jsx(ChevronRight, { className: cn("h-3 w-3 transition-transform", open && "rotate-90") }),
|
|
448
|
-
label
|
|
449
|
-
]
|
|
450
|
-
}
|
|
451
|
-
),
|
|
452
|
-
action && /* @__PURE__ */ jsx("div", { className: "shrink-0", children: action })
|
|
453
|
-
] }),
|
|
454
|
-
open && /* @__PURE__ */ jsx("div", { children })
|
|
455
|
-
] });
|
|
456
|
-
}
|
|
457
|
-
__name(CollapsibleSection, "CollapsibleSection");
|
|
458
|
-
|
|
459
|
-
// src/tools/OpenapiViewer/components/DocsLayout/anchor.ts
|
|
460
|
-
function endpointAnchor(ep) {
|
|
461
|
-
const slug = ep.path.replace(/^https?:\/\/[^/]+/, "").replace(/[{}]/g, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase();
|
|
462
|
-
return `ep-${ep.method.toLowerCase()}-${slug}`;
|
|
463
|
-
}
|
|
464
|
-
__name(endpointAnchor, "endpointAnchor");
|
|
465
|
-
|
|
466
|
-
// src/tools/OpenapiViewer/components/DocsLayout/sidebarLabel.ts
|
|
467
|
-
function longestCommonPrefix(paths) {
|
|
468
|
-
if (paths.length === 0) return "";
|
|
469
|
-
if (paths.length === 1) return "";
|
|
470
|
-
const segments = paths.map((p) => p.split("/"));
|
|
471
|
-
const minLen = Math.min(...segments.map((s) => s.length));
|
|
472
|
-
const shared = [];
|
|
473
|
-
for (let i = 0; i < minLen; i++) {
|
|
474
|
-
const first = segments[0][i];
|
|
475
|
-
if (segments.every((s) => s[i] === first)) {
|
|
476
|
-
shared.push(first);
|
|
477
|
-
} else {
|
|
478
|
-
break;
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
const joined = shared.join("/");
|
|
482
|
-
return joined;
|
|
483
|
-
}
|
|
484
|
-
__name(longestCommonPrefix, "longestCommonPrefix");
|
|
485
|
-
function sidebarLabel(ep, groupCommonPrefix) {
|
|
486
|
-
if (ep.summary) return ep.summary;
|
|
487
|
-
if (groupCommonPrefix && ep.path.startsWith(groupCommonPrefix)) {
|
|
488
|
-
const tail = ep.path.slice(groupCommonPrefix.length) || "/";
|
|
489
|
-
return tail;
|
|
490
|
-
}
|
|
491
|
-
return relativePath2(ep.path);
|
|
492
|
-
}
|
|
493
|
-
__name(sidebarLabel, "sidebarLabel");
|
|
494
|
-
function relativePath2(full) {
|
|
495
|
-
try {
|
|
496
|
-
return new URL(full).pathname;
|
|
497
|
-
} catch {
|
|
498
|
-
return full;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
__name(relativePath2, "relativePath");
|
|
502
|
-
function sidebarTooltip(ep) {
|
|
503
|
-
return `${ep.method} ${relativePath2(ep.path)}`;
|
|
504
|
-
}
|
|
505
|
-
__name(sidebarTooltip, "sidebarTooltip");
|
|
506
|
-
var METHOD_ORDER = {
|
|
507
|
-
GET: 0,
|
|
508
|
-
POST: 1,
|
|
509
|
-
PUT: 2,
|
|
510
|
-
PATCH: 3,
|
|
511
|
-
DELETE: 4
|
|
512
|
-
};
|
|
513
|
-
function groupEndpoints(list) {
|
|
514
|
-
const map = /* @__PURE__ */ new Map();
|
|
515
|
-
for (const ep of list) {
|
|
516
|
-
const arr = map.get(ep.category) ?? [];
|
|
517
|
-
arr.push(ep);
|
|
518
|
-
map.set(ep.category, arr);
|
|
519
|
-
}
|
|
520
|
-
const groups = Array.from(map.entries()).map(([category, endpoints]) => ({
|
|
521
|
-
category,
|
|
522
|
-
endpoints: [...endpoints].sort((a, b) => {
|
|
523
|
-
const byPath = a.path.localeCompare(b.path);
|
|
524
|
-
if (byPath !== 0) return byPath;
|
|
525
|
-
return (METHOD_ORDER[a.method] ?? 99) - (METHOD_ORDER[b.method] ?? 99);
|
|
526
|
-
}),
|
|
527
|
-
commonPrefix: longestCommonPrefix(endpoints.map((e) => e.path))
|
|
528
|
-
}));
|
|
529
|
-
groups.sort((a, b) => {
|
|
530
|
-
if (a.category === "Other") return 1;
|
|
531
|
-
if (b.category === "Other") return -1;
|
|
532
|
-
return a.category.localeCompare(b.category);
|
|
533
|
-
});
|
|
534
|
-
return groups;
|
|
535
|
-
}
|
|
536
|
-
__name(groupEndpoints, "groupEndpoints");
|
|
537
|
-
function DocsSidebar({
|
|
538
|
-
info,
|
|
539
|
-
endpoints,
|
|
540
|
-
schemas,
|
|
541
|
-
currentSchemaId,
|
|
542
|
-
onSchemaChange,
|
|
543
|
-
activeEndpointId,
|
|
544
|
-
selectedVersion,
|
|
545
|
-
onNavigate
|
|
546
|
-
}) {
|
|
547
|
-
const [search, setSearch] = useState("");
|
|
548
|
-
const [debounced, setDebounced] = useState("");
|
|
549
|
-
useEffect(() => {
|
|
550
|
-
const id = setTimeout(() => setDebounced(search), 120);
|
|
551
|
-
return () => clearTimeout(id);
|
|
552
|
-
}, [search]);
|
|
553
|
-
const filteredGroups = useMemo(() => {
|
|
554
|
-
let list = deduplicateEndpoints(endpoints, selectedVersion);
|
|
555
|
-
if (debounced) {
|
|
556
|
-
const q = debounced.toLowerCase();
|
|
557
|
-
list = list.filter(
|
|
558
|
-
(e) => e.summary.toLowerCase().includes(q) || e.name.toLowerCase().includes(q) || e.description.toLowerCase().includes(q) || e.path.toLowerCase().includes(q)
|
|
559
|
-
);
|
|
560
|
-
}
|
|
561
|
-
return groupEndpoints(list);
|
|
562
|
-
}, [endpoints, debounced, selectedVersion]);
|
|
563
|
-
const schemaOptions = useMemo(
|
|
564
|
-
() => schemas.map((s) => ({ value: s.id, label: s.name })),
|
|
565
|
-
[schemas]
|
|
566
|
-
);
|
|
567
|
-
const hasMultipleSchemas = schemas.length > 1;
|
|
568
|
-
const apiTitle = info?.title ?? "API Reference";
|
|
569
|
-
return /* @__PURE__ */ jsxs("aside", { className: "flex flex-col min-h-0 border-r bg-muted/10", children: [
|
|
570
|
-
/* @__PURE__ */ jsxs("div", { className: "shrink-0 border-b px-4 h-12 flex items-center gap-2", children: [
|
|
571
|
-
/* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold text-foreground truncate", children: apiTitle }),
|
|
572
|
-
info?.version && /* @__PURE__ */ jsxs("span", { className: "font-mono text-[10px] text-muted-foreground/70 shrink-0", children: [
|
|
573
|
-
"v",
|
|
574
|
-
info.version
|
|
575
|
-
] })
|
|
576
|
-
] }),
|
|
577
|
-
/* @__PURE__ */ jsxs("div", { className: "shrink-0 border-b px-3 py-3 space-y-2", children: [
|
|
578
|
-
hasMultipleSchemas && /* @__PURE__ */ jsx(
|
|
579
|
-
Combobox,
|
|
580
|
-
{
|
|
581
|
-
options: schemaOptions,
|
|
582
|
-
value: currentSchemaId ?? "",
|
|
583
|
-
onValueChange: (id) => id && onSchemaChange(id),
|
|
584
|
-
placeholder: "Select API",
|
|
585
|
-
searchPlaceholder: "Search APIs\u2026",
|
|
586
|
-
emptyText: "No APIs found",
|
|
587
|
-
className: "w-full h-8 text-xs"
|
|
588
|
-
}
|
|
589
|
-
),
|
|
590
|
-
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
591
|
-
/* @__PURE__ */ jsx(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" }),
|
|
592
|
-
/* @__PURE__ */ jsx(
|
|
593
|
-
Input,
|
|
594
|
-
{
|
|
595
|
-
placeholder: "Search endpoints\u2026",
|
|
596
|
-
value: search,
|
|
597
|
-
onChange: (e) => setSearch(e.target.value),
|
|
598
|
-
className: "pl-8 h-8 text-xs"
|
|
599
|
-
}
|
|
600
|
-
)
|
|
601
|
-
] })
|
|
602
|
-
] }),
|
|
603
|
-
/* @__PURE__ */ jsx(ScrollArea, { children: filteredGroups.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-10 px-4 text-center text-xs text-muted-foreground", children: debounced ? `No endpoints match "${debounced}"` : "No endpoints in this schema" }) : /* @__PURE__ */ jsx("nav", { className: "py-2", children: filteredGroups.map((group) => /* @__PURE__ */ jsxs("div", { className: "mb-4 last:mb-2", children: [
|
|
604
|
-
/* @__PURE__ */ jsx("div", { className: "px-4 py-1.5 text-[10px] font-semibold uppercase tracking-[0.14em] text-muted-foreground/50 select-none", children: group.category }),
|
|
605
|
-
/* @__PURE__ */ jsx("div", { children: group.endpoints.map((ep) => {
|
|
606
|
-
const anchor = endpointAnchor(ep);
|
|
607
|
-
const isActive = activeEndpointId === anchor;
|
|
608
|
-
const label = sidebarLabel(ep, group.commonPrefix);
|
|
609
|
-
const tooltip = sidebarTooltip(ep);
|
|
610
|
-
const useMono = !ep.summary;
|
|
611
|
-
return /* @__PURE__ */ jsxs(Tooltip, { delayDuration: 350, children: [
|
|
612
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
613
|
-
"button",
|
|
614
|
-
{
|
|
615
|
-
onClick: () => onNavigate(anchor),
|
|
616
|
-
"aria-current": isActive ? "location" : void 0,
|
|
617
|
-
className: cn(
|
|
618
|
-
"relative group w-full text-left flex items-center gap-2 pl-4 pr-3 py-1.5 transition-colors",
|
|
619
|
-
isActive ? "bg-primary/10 text-foreground" : "hover:bg-muted/40 text-foreground/75 hover:text-foreground"
|
|
620
|
-
),
|
|
621
|
-
children: [
|
|
622
|
-
isActive && /* @__PURE__ */ jsx("span", { className: "absolute left-0 top-1 bottom-1 w-0.5 rounded-r bg-primary" }),
|
|
623
|
-
/* @__PURE__ */ jsx(MethodBadge, { method: ep.method }),
|
|
624
|
-
/* @__PURE__ */ jsx(
|
|
625
|
-
"span",
|
|
626
|
-
{
|
|
627
|
-
className: cn(
|
|
628
|
-
"truncate leading-tight flex-1 min-w-0",
|
|
629
|
-
useMono ? "font-mono text-[11px]" : "text-[12px]",
|
|
630
|
-
isActive && "text-foreground font-medium"
|
|
631
|
-
),
|
|
632
|
-
children: label
|
|
633
|
-
}
|
|
634
|
-
)
|
|
635
|
-
]
|
|
636
|
-
}
|
|
637
|
-
) }),
|
|
638
|
-
/* @__PURE__ */ jsx(TooltipContent, { side: "right", align: "center", className: "font-mono text-[11px]", children: tooltip })
|
|
639
|
-
] }, `${ep.method}-${ep.path}`);
|
|
640
|
-
}) })
|
|
641
|
-
] }, group.category)) }) })
|
|
642
|
-
] });
|
|
643
|
-
}
|
|
644
|
-
__name(DocsSidebar, "DocsSidebar");
|
|
645
|
-
var FLAVOUR_LABELS = {
|
|
646
|
-
markdown: {
|
|
647
|
-
title: "Markdown for LLM",
|
|
648
|
-
hint: "Endpoints + params as prose. Smallest."
|
|
649
|
-
},
|
|
650
|
-
compact: {
|
|
651
|
-
title: "Compact JSON",
|
|
652
|
-
hint: "Dereferenced, no examples, minified."
|
|
653
|
-
},
|
|
654
|
-
raw: {
|
|
655
|
-
title: "Raw JSON",
|
|
656
|
-
hint: "Full OpenAPI document with $refs."
|
|
657
|
-
}
|
|
658
|
-
};
|
|
659
|
-
function SchemaCopyMenu({ schema, endpoints, baseUrl }) {
|
|
660
|
-
const [sizeCache, setSizeCache] = useState({});
|
|
661
|
-
const [justCopied, setJustCopied] = useState(null);
|
|
662
|
-
const [open, setOpen] = useState(false);
|
|
663
|
-
const isReady = schema !== null && endpoints.length > 0;
|
|
664
|
-
const build = useCallback(
|
|
665
|
-
(flavour) => {
|
|
666
|
-
if (!schema) return "";
|
|
667
|
-
if (flavour === "markdown") return toMarkdown(schema, endpoints, baseUrl);
|
|
668
|
-
if (flavour === "compact") return toCompactJson(schema, baseUrl);
|
|
669
|
-
return toRawJson(schema, baseUrl);
|
|
670
|
-
},
|
|
671
|
-
[schema, endpoints, baseUrl]
|
|
672
|
-
);
|
|
673
|
-
const handleCopy = useCallback(
|
|
674
|
-
async (flavour) => {
|
|
675
|
-
if (!isReady) return;
|
|
676
|
-
const text = build(flavour);
|
|
677
|
-
try {
|
|
678
|
-
await navigator.clipboard.writeText(text);
|
|
679
|
-
setSizeCache((prev) => ({ ...prev, [flavour]: formatBytes(text) }));
|
|
680
|
-
setJustCopied(flavour);
|
|
681
|
-
setTimeout(() => setJustCopied(null), 1500);
|
|
682
|
-
setOpen(false);
|
|
683
|
-
} catch {
|
|
684
|
-
}
|
|
685
|
-
},
|
|
686
|
-
[build, isReady]
|
|
687
|
-
);
|
|
688
|
-
const flavours = useMemo(() => ["markdown", "compact", "raw"], []);
|
|
689
|
-
return /* @__PURE__ */ jsxs(DropdownMenu, { open, onOpenChange: setOpen, children: [
|
|
690
|
-
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1.5 text-xs", disabled: !isReady, children: [
|
|
691
|
-
/* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }),
|
|
692
|
-
"Copy for AI",
|
|
693
|
-
/* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 opacity-60" })
|
|
694
|
-
] }) }),
|
|
695
|
-
/* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-72", children: [
|
|
696
|
-
/* @__PURE__ */ jsx(DropdownMenuLabel, { className: "text-[10px] uppercase tracking-wider text-muted-foreground/70", children: "Copy schema" }),
|
|
697
|
-
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
698
|
-
flavours.map((f) => {
|
|
699
|
-
const label = FLAVOUR_LABELS[f];
|
|
700
|
-
const size = sizeCache[f];
|
|
701
|
-
const isDone = justCopied === f;
|
|
702
|
-
return /* @__PURE__ */ jsxs(
|
|
703
|
-
DropdownMenuItem,
|
|
704
|
-
{
|
|
705
|
-
onClick: (e) => {
|
|
706
|
-
e.preventDefault();
|
|
707
|
-
void handleCopy(f);
|
|
708
|
-
},
|
|
709
|
-
className: "flex flex-col items-start gap-0.5 py-2 cursor-pointer",
|
|
710
|
-
children: [
|
|
711
|
-
/* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-2", children: [
|
|
712
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium flex-1", children: label.title }),
|
|
713
|
-
isDone ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 text-[10px] text-emerald-500", children: [
|
|
714
|
-
/* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }),
|
|
715
|
-
" Copied"
|
|
716
|
-
] }) : size ? /* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground/70 tabular-nums", children: size }) : null
|
|
717
|
-
] }),
|
|
718
|
-
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground/70 leading-snug", children: label.hint })
|
|
719
|
-
]
|
|
720
|
-
},
|
|
721
|
-
f
|
|
722
|
-
);
|
|
723
|
-
})
|
|
724
|
-
] })
|
|
725
|
-
] });
|
|
726
|
-
}
|
|
727
|
-
__name(SchemaCopyMenu, "SchemaCopyMenu");
|
|
728
|
-
function ApiIntroSection({ info, schema, endpoints, resolvedBaseUrl }) {
|
|
729
|
-
return /* @__PURE__ */ jsxs("section", { className: "pb-10 mb-10 border-b", children: [
|
|
730
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4 flex-wrap", children: [
|
|
731
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 flex-wrap min-w-0", children: [
|
|
732
|
-
/* @__PURE__ */ jsx("h1", { className: "text-3xl font-semibold tracking-tight text-foreground leading-tight", children: info.title }),
|
|
733
|
-
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center rounded-full bg-muted px-2 py-0.5 font-mono text-[11px] text-muted-foreground", children: [
|
|
734
|
-
"v",
|
|
735
|
-
info.version
|
|
736
|
-
] })
|
|
737
|
-
] }),
|
|
738
|
-
/* @__PURE__ */ jsx(
|
|
739
|
-
SchemaCopyMenu,
|
|
740
|
-
{
|
|
741
|
-
schema,
|
|
742
|
-
endpoints,
|
|
743
|
-
baseUrl: resolvedBaseUrl
|
|
744
|
-
}
|
|
745
|
-
)
|
|
746
|
-
] }),
|
|
747
|
-
info.description && /* @__PURE__ */ jsx("div", { className: "mt-4 text-muted-foreground", children: /* @__PURE__ */ jsx(MarkdownMessage, { content: info.description }) }),
|
|
748
|
-
info.servers && info.servers.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-6 space-y-2", children: [
|
|
749
|
-
/* @__PURE__ */ jsx("h4", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/60", children: "Base URL" }),
|
|
750
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: info.servers.map((s, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2 flex-wrap", children: [
|
|
751
|
-
/* @__PURE__ */ jsx("code", { className: "font-mono text-xs px-2 py-1 rounded bg-muted border", children: s.url }),
|
|
752
|
-
s.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: s.description })
|
|
753
|
-
] }, `${s.url}-${i}`)) })
|
|
754
|
-
] })
|
|
755
|
-
] });
|
|
756
|
-
}
|
|
757
|
-
__name(ApiIntroSection, "ApiIntroSection");
|
|
758
|
-
|
|
759
|
-
// src/tools/OpenapiViewer/components/DocsLayout/schemaFields.ts
|
|
760
|
-
var MAX_DEPTH = 2;
|
|
761
|
-
function describeType(node) {
|
|
762
|
-
if (!node.type && node.properties) return "object";
|
|
763
|
-
const base = node.type || "any";
|
|
764
|
-
if (base === "array") {
|
|
765
|
-
const itemType = node.items ? describeType(node.items) : "any";
|
|
766
|
-
return `array<${itemType}>`;
|
|
767
|
-
}
|
|
768
|
-
if (Array.isArray(node.enum) && node.enum.length > 0) {
|
|
769
|
-
return `${base} enum`;
|
|
770
|
-
}
|
|
771
|
-
if (node.format) return `${base} (${node.format})`;
|
|
772
|
-
return base;
|
|
773
|
-
}
|
|
774
|
-
__name(describeType, "describeType");
|
|
775
|
-
function schemaToFields(schema, prefix = "", depth = 0) {
|
|
776
|
-
if (!schema || depth > MAX_DEPTH) return [];
|
|
777
|
-
if (schema.type === "array") {
|
|
778
|
-
if (!schema.items) {
|
|
779
|
-
return [{ name: prefix || "[]", type: "array", required: false }];
|
|
780
|
-
}
|
|
781
|
-
const inner = schemaToFields(schema.items, prefix ? `${prefix}[]` : "[]", depth);
|
|
782
|
-
if (inner.length === 0) {
|
|
783
|
-
return [
|
|
784
|
-
{
|
|
785
|
-
name: prefix || "[]",
|
|
786
|
-
type: describeType(schema),
|
|
787
|
-
required: false,
|
|
788
|
-
description: schema.description
|
|
789
|
-
}
|
|
790
|
-
];
|
|
791
|
-
}
|
|
792
|
-
return inner;
|
|
793
|
-
}
|
|
794
|
-
if (schema.type !== "object" && !schema.properties) {
|
|
795
|
-
return [
|
|
796
|
-
{
|
|
797
|
-
name: prefix || "(body)",
|
|
798
|
-
type: describeType(schema),
|
|
799
|
-
required: false,
|
|
800
|
-
description: schema.description
|
|
801
|
-
}
|
|
802
|
-
];
|
|
803
|
-
}
|
|
804
|
-
const required = new Set(schema.required ?? []);
|
|
805
|
-
const rows = [];
|
|
806
|
-
const props = schema.properties ?? {};
|
|
807
|
-
for (const [key, node] of Object.entries(props)) {
|
|
808
|
-
const fullName = prefix ? `${prefix}.${key}` : key;
|
|
809
|
-
const isRequired = required.has(key);
|
|
810
|
-
const isNestedExpandable = node.type === "object" && node.properties || node.type === "array" && node.items;
|
|
811
|
-
if (isNestedExpandable && depth < MAX_DEPTH) {
|
|
812
|
-
rows.push({
|
|
813
|
-
name: fullName,
|
|
814
|
-
type: describeType(node),
|
|
815
|
-
required: isRequired,
|
|
816
|
-
description: node.description
|
|
817
|
-
});
|
|
818
|
-
rows.push(...schemaToFields(node, fullName, depth + 1));
|
|
819
|
-
} else {
|
|
820
|
-
rows.push({
|
|
821
|
-
name: fullName,
|
|
822
|
-
type: describeType(node),
|
|
823
|
-
required: isRequired,
|
|
824
|
-
description: node.description
|
|
825
|
-
});
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
return rows;
|
|
829
|
-
}
|
|
830
|
-
__name(schemaToFields, "schemaToFields");
|
|
831
|
-
function EndpointDoc({ endpoint, isLoadedInPlayground, onTryIt }) {
|
|
832
|
-
const anchor = endpointAnchor(endpoint);
|
|
833
|
-
const pathParams = endpoint.parameters?.filter((p) => endpoint.path.includes(`{${p.name}}`)) ?? [];
|
|
834
|
-
const queryParams = endpoint.parameters?.filter((p) => !endpoint.path.includes(`{${p.name}}`)) ?? [];
|
|
835
|
-
const [copied, setCopied] = useState(false);
|
|
836
|
-
const copyAnchor = useCallback(() => {
|
|
837
|
-
if (typeof window === "undefined") return;
|
|
838
|
-
const url = `${window.location.origin}${window.location.pathname}#${anchor}`;
|
|
839
|
-
void navigator.clipboard?.writeText(url).then(() => {
|
|
840
|
-
setCopied(true);
|
|
841
|
-
setTimeout(() => setCopied(false), 1200);
|
|
842
|
-
});
|
|
843
|
-
}, [anchor]);
|
|
844
|
-
const endpointMd = useMemo(() => endpointToMarkdown(endpoint), [endpoint]);
|
|
845
|
-
return /* @__PURE__ */ jsxs(
|
|
846
|
-
"section",
|
|
847
|
-
{
|
|
848
|
-
id: anchor,
|
|
849
|
-
"data-endpoint-anchor": anchor,
|
|
850
|
-
className: "scroll-mt-24 py-10 first:pt-0",
|
|
851
|
-
children: [
|
|
852
|
-
/* @__PURE__ */ jsxs("header", { className: "space-y-4", children: [
|
|
853
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 flex-wrap", children: [
|
|
854
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2.5 min-w-0 flex-1 group/header", children: [
|
|
855
|
-
/* @__PURE__ */ jsx("span", { className: "shrink-0 translate-y-[2px]", children: /* @__PURE__ */ jsx(MethodBadge, { method: endpoint.method }) }),
|
|
856
|
-
/* @__PURE__ */ jsx(
|
|
857
|
-
"code",
|
|
858
|
-
{
|
|
859
|
-
className: "font-mono text-sm md:text-[15px] font-medium text-foreground leading-snug min-w-0",
|
|
860
|
-
style: { overflowWrap: "anywhere", wordBreak: "break-word" },
|
|
861
|
-
children: relativePath(endpoint.path)
|
|
862
|
-
}
|
|
863
|
-
),
|
|
864
|
-
/* @__PURE__ */ jsx(
|
|
865
|
-
"button",
|
|
866
|
-
{
|
|
867
|
-
type: "button",
|
|
868
|
-
onClick: copyAnchor,
|
|
869
|
-
title: "Copy link to this section",
|
|
870
|
-
className: cn(
|
|
871
|
-
"shrink-0 p-1 rounded text-muted-foreground/40 hover:text-foreground hover:bg-muted transition-all",
|
|
872
|
-
"opacity-0 group-hover/header:opacity-100",
|
|
873
|
-
copied && "opacity-100 text-emerald-500"
|
|
874
|
-
),
|
|
875
|
-
children: copied ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Link2, { className: "h-3.5 w-3.5" })
|
|
876
|
-
}
|
|
877
|
-
),
|
|
878
|
-
/* @__PURE__ */ jsx(
|
|
879
|
-
CopyButton,
|
|
880
|
-
{
|
|
881
|
-
value: endpointMd,
|
|
882
|
-
title: "Copy endpoint as markdown (for AI)",
|
|
883
|
-
variant: "ghost",
|
|
884
|
-
size: "icon",
|
|
885
|
-
iconClassName: "h-3.5 w-3.5",
|
|
886
|
-
className: cn(
|
|
887
|
-
"shrink-0 h-6 w-6 text-muted-foreground/40 hover:text-foreground",
|
|
888
|
-
"opacity-0 group-hover/header:opacity-100 focus-visible:opacity-100"
|
|
889
|
-
)
|
|
890
|
-
}
|
|
891
|
-
)
|
|
892
|
-
] }),
|
|
893
|
-
/* @__PURE__ */ jsxs(
|
|
894
|
-
Button,
|
|
895
|
-
{
|
|
896
|
-
size: "sm",
|
|
897
|
-
variant: isLoadedInPlayground ? "secondary" : "default",
|
|
898
|
-
onClick: onTryIt,
|
|
899
|
-
className: "shrink-0 h-8 text-xs gap-1.5 lg:flex hidden",
|
|
900
|
-
children: [
|
|
901
|
-
/* @__PURE__ */ jsx(Play, { className: "h-3 w-3" }),
|
|
902
|
-
isLoadedInPlayground ? "Loaded" : "Try it"
|
|
903
|
-
]
|
|
904
|
-
}
|
|
905
|
-
)
|
|
906
|
-
] }),
|
|
907
|
-
endpoint.description && /* @__PURE__ */ jsx("div", { className: "text-muted-foreground", children: /* @__PURE__ */ jsx(MarkdownMessage, { content: endpoint.description }) }),
|
|
908
|
-
/* @__PURE__ */ jsxs(
|
|
909
|
-
Button,
|
|
910
|
-
{
|
|
911
|
-
size: "sm",
|
|
912
|
-
variant: isLoadedInPlayground ? "secondary" : "default",
|
|
913
|
-
onClick: onTryIt,
|
|
914
|
-
className: "lg:hidden h-8 text-xs gap-1.5",
|
|
915
|
-
children: [
|
|
916
|
-
/* @__PURE__ */ jsx(Play, { className: "h-3 w-3" }),
|
|
917
|
-
isLoadedInPlayground ? "Loaded in playground" : "Try it"
|
|
918
|
-
]
|
|
919
|
-
}
|
|
920
|
-
)
|
|
921
|
-
] }),
|
|
922
|
-
(pathParams.length > 0 || queryParams.length > 0 || endpoint.requestBody || endpoint.responses?.length) && /* @__PURE__ */ jsxs("div", { className: "mt-8 space-y-8", children: [
|
|
923
|
-
pathParams.length > 0 && /* @__PURE__ */ jsx(ParamTable, { title: "Path parameters", params: pathParams }),
|
|
924
|
-
queryParams.length > 0 && /* @__PURE__ */ jsx(ParamTable, { title: "Query parameters", params: queryParams }),
|
|
925
|
-
endpoint.requestBody && /* @__PURE__ */ jsx(RequestBodySection, { body: endpoint.requestBody }),
|
|
926
|
-
endpoint.responses && endpoint.responses.length > 0 && /* @__PURE__ */ jsx(Subsection, { title: "Responses", children: /* @__PURE__ */ jsx("div", { className: "divide-y border rounded-md overflow-hidden", children: endpoint.responses.map((r) => /* @__PURE__ */ jsxs(
|
|
927
|
-
"div",
|
|
928
|
-
{
|
|
929
|
-
className: "grid grid-cols-[72px_minmax(0,1fr)] items-center gap-3 px-3 py-2.5 bg-background",
|
|
930
|
-
children: [
|
|
931
|
-
/* @__PURE__ */ jsx("div", { className: "flex justify-start", children: /* @__PURE__ */ jsx(StatusTag, { code: r.code }) }),
|
|
932
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground leading-relaxed break-words", children: r.description })
|
|
933
|
-
]
|
|
934
|
-
},
|
|
935
|
-
r.code
|
|
936
|
-
)) }) })
|
|
937
|
-
] })
|
|
938
|
-
]
|
|
939
|
-
}
|
|
940
|
-
);
|
|
941
|
-
}
|
|
942
|
-
__name(EndpointDoc, "EndpointDoc");
|
|
943
|
-
function RequestBodySection({ body }) {
|
|
944
|
-
const fields = useMemo(() => schemaToFields(body.schema), [body.schema]);
|
|
945
|
-
const typeLabel = body.schema ? body.type === "array" ? `array<${body.schema.items?.type ?? "object"}>` : body.type : body.type;
|
|
946
|
-
return /* @__PURE__ */ jsx(Subsection, { title: "Request body", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
947
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2 flex-wrap", children: [
|
|
948
|
-
/* @__PURE__ */ jsx("code", { className: "font-mono text-[11px] text-muted-foreground/80", children: typeLabel }),
|
|
949
|
-
body.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: body.description })
|
|
950
|
-
] }),
|
|
951
|
-
fields.length > 0 && /* @__PURE__ */ jsx(FieldsTable, { fields })
|
|
952
|
-
] }) });
|
|
953
|
-
}
|
|
954
|
-
__name(RequestBodySection, "RequestBodySection");
|
|
955
|
-
function FieldsTable({ fields }) {
|
|
956
|
-
return /* @__PURE__ */ jsx("div", { className: "divide-y border rounded-md overflow-hidden", children: fields.map((f) => /* @__PURE__ */ jsxs("div", { className: "px-3 py-2.5 bg-background space-y-1", children: [
|
|
957
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2 flex-wrap", children: [
|
|
958
|
-
/* @__PURE__ */ jsx("code", { className: "font-mono text-xs font-medium text-foreground", children: f.name }),
|
|
959
|
-
f.required && /* @__PURE__ */ jsx(
|
|
960
|
-
"span",
|
|
961
|
-
{
|
|
962
|
-
title: "Required",
|
|
963
|
-
className: "text-[9px] text-destructive font-bold leading-none",
|
|
964
|
-
children: "*"
|
|
965
|
-
}
|
|
966
|
-
),
|
|
967
|
-
/* @__PURE__ */ jsx("code", { className: "font-mono text-[11px] text-muted-foreground/70", children: f.type })
|
|
968
|
-
] }),
|
|
969
|
-
f.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground leading-relaxed break-words", children: f.description })
|
|
970
|
-
] }, f.name)) });
|
|
971
|
-
}
|
|
972
|
-
__name(FieldsTable, "FieldsTable");
|
|
973
|
-
function Subsection({ title, children }) {
|
|
974
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-2.5", children: [
|
|
975
|
-
/* @__PURE__ */ jsx("h4", { className: "text-[10px] font-semibold uppercase tracking-[0.12em] text-muted-foreground/70", children: title }),
|
|
976
|
-
children
|
|
977
|
-
] });
|
|
978
|
-
}
|
|
979
|
-
__name(Subsection, "Subsection");
|
|
980
|
-
function ParamTable({
|
|
981
|
-
title,
|
|
982
|
-
params
|
|
983
|
-
}) {
|
|
984
|
-
return /* @__PURE__ */ jsx(Subsection, { title, children: /* @__PURE__ */ jsx("div", { className: "divide-y border rounded-md overflow-hidden", children: params.map((p) => /* @__PURE__ */ jsxs("div", { className: "px-3 py-2.5 bg-background space-y-1", children: [
|
|
985
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2 flex-wrap", children: [
|
|
986
|
-
/* @__PURE__ */ jsx("code", { className: "font-mono text-xs font-medium text-foreground", children: p.name }),
|
|
987
|
-
p.required && /* @__PURE__ */ jsx(
|
|
988
|
-
"span",
|
|
989
|
-
{
|
|
990
|
-
title: "Required",
|
|
991
|
-
className: "text-[9px] text-destructive font-bold leading-none",
|
|
992
|
-
children: "*"
|
|
993
|
-
}
|
|
994
|
-
),
|
|
995
|
-
/* @__PURE__ */ jsx("code", { className: "font-mono text-[11px] text-muted-foreground/70", children: p.type })
|
|
996
|
-
] }),
|
|
997
|
-
p.description ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground leading-relaxed break-words", children: p.description }) : null
|
|
998
|
-
] }, p.name)) }) });
|
|
999
|
-
}
|
|
1000
|
-
__name(ParamTable, "ParamTable");
|
|
1001
|
-
function StatusTag({ code }) {
|
|
1002
|
-
const numeric = Number.parseInt(code, 10);
|
|
1003
|
-
const cls = !Number.isFinite(numeric) ? "bg-muted text-muted-foreground border-border" : numeric >= 500 ? "bg-red-500/10 text-red-600 dark:text-red-400 border-red-500/25" : numeric >= 400 ? "bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/25" : numeric >= 300 ? "bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/25" : "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/25";
|
|
1004
|
-
return /* @__PURE__ */ jsx("span", { className: cn(
|
|
1005
|
-
"inline-flex items-center justify-center rounded border px-2 py-0.5 font-mono text-[11px] font-bold leading-none shrink-0 tabular-nums",
|
|
1006
|
-
cls
|
|
1007
|
-
), children: code });
|
|
1008
|
-
}
|
|
1009
|
-
__name(StatusTag, "StatusTag");
|
|
1010
|
-
var DocsView = React6.forwardRef(/* @__PURE__ */ __name(function DocsView2({
|
|
1011
|
-
info,
|
|
1012
|
-
rawSchema,
|
|
1013
|
-
resolvedBaseUrl,
|
|
1014
|
-
endpoints,
|
|
1015
|
-
selectedVersion,
|
|
1016
|
-
loadedEndpoint,
|
|
1017
|
-
onTryEndpoint,
|
|
1018
|
-
onActiveChange
|
|
1019
|
-
}, ref) {
|
|
1020
|
-
const scrollRef = useRef(null);
|
|
1021
|
-
const visibleEndpoints = useMemo(
|
|
1022
|
-
() => deduplicateEndpoints(endpoints, selectedVersion),
|
|
1023
|
-
[endpoints, selectedVersion]
|
|
1024
|
-
);
|
|
1025
|
-
const scrollToAnchor = useCallback((anchor) => {
|
|
1026
|
-
const root = scrollRef.current;
|
|
1027
|
-
if (!root) return;
|
|
1028
|
-
const el = root.querySelector(`[data-endpoint-anchor="${anchor}"]`);
|
|
1029
|
-
if (!el) return;
|
|
1030
|
-
el.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
1031
|
-
}, []);
|
|
1032
|
-
React6.useImperativeHandle(ref, () => ({ scrollToAnchor }), [scrollToAnchor]);
|
|
1033
|
-
useEffect(() => {
|
|
1034
|
-
const root = scrollRef.current;
|
|
1035
|
-
if (!root) return;
|
|
1036
|
-
let rafId = 0;
|
|
1037
|
-
let lastActive = null;
|
|
1038
|
-
const compute = /* @__PURE__ */ __name(() => {
|
|
1039
|
-
rafId = 0;
|
|
1040
|
-
const sections = root.querySelectorAll("[data-endpoint-anchor]");
|
|
1041
|
-
if (sections.length === 0) return;
|
|
1042
|
-
const rootTop = root.getBoundingClientRect().top;
|
|
1043
|
-
const threshold = rootTop + root.clientHeight * 0.25;
|
|
1044
|
-
let active = null;
|
|
1045
|
-
for (const s of Array.from(sections)) {
|
|
1046
|
-
const top = s.getBoundingClientRect().top;
|
|
1047
|
-
if (top <= threshold) {
|
|
1048
|
-
active = s.dataset.endpointAnchor ?? null;
|
|
1049
|
-
} else {
|
|
1050
|
-
break;
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
if (active !== lastActive) {
|
|
1054
|
-
lastActive = active;
|
|
1055
|
-
onActiveChange(active);
|
|
1056
|
-
}
|
|
1057
|
-
}, "compute");
|
|
1058
|
-
const onScroll = /* @__PURE__ */ __name(() => {
|
|
1059
|
-
if (rafId) return;
|
|
1060
|
-
rafId = requestAnimationFrame(compute);
|
|
1061
|
-
}, "onScroll");
|
|
1062
|
-
compute();
|
|
1063
|
-
root.addEventListener("scroll", onScroll, { passive: true });
|
|
1064
|
-
return () => {
|
|
1065
|
-
root.removeEventListener("scroll", onScroll);
|
|
1066
|
-
if (rafId) cancelAnimationFrame(rafId);
|
|
1067
|
-
};
|
|
1068
|
-
}, [visibleEndpoints, onActiveChange]);
|
|
1069
|
-
return /* @__PURE__ */ jsx("div", { ref: scrollRef, className: "flex-1 overflow-y-auto min-h-0", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-[860px] px-6 md:px-10 lg:px-14 py-12", children: [
|
|
1070
|
-
info && /* @__PURE__ */ jsx(
|
|
1071
|
-
ApiIntroSection,
|
|
1072
|
-
{
|
|
1073
|
-
info,
|
|
1074
|
-
schema: rawSchema,
|
|
1075
|
-
endpoints: visibleEndpoints,
|
|
1076
|
-
resolvedBaseUrl
|
|
1077
|
-
}
|
|
1078
|
-
),
|
|
1079
|
-
visibleEndpoints.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-16 text-center text-sm text-muted-foreground", children: "No endpoints to display." }) : /* @__PURE__ */ jsx("div", { className: "divide-y divide-border/60", children: visibleEndpoints.map((ep) => {
|
|
1080
|
-
const isLoaded = loadedEndpoint?.method === ep.method && loadedEndpoint?.path === ep.path;
|
|
1081
|
-
return /* @__PURE__ */ jsx(
|
|
1082
|
-
EndpointDoc,
|
|
1083
|
-
{
|
|
1084
|
-
endpoint: ep,
|
|
1085
|
-
isLoadedInPlayground: isLoaded,
|
|
1086
|
-
onTryIt: () => onTryEndpoint(ep)
|
|
1087
|
-
},
|
|
1088
|
-
`${ep.method}-${ep.path}`
|
|
1089
|
-
);
|
|
1090
|
-
}) })
|
|
1091
|
-
] }) });
|
|
1092
|
-
}, "DocsView"));
|
|
1093
|
-
var MAX_DEPTH2 = 6;
|
|
1094
|
-
function defaultForSchema(schema) {
|
|
1095
|
-
if (!schema) return null;
|
|
1096
|
-
if (Array.isArray(schema.enum) && schema.enum.length > 0) return schema.enum[0];
|
|
1097
|
-
switch (schema.type) {
|
|
1098
|
-
case "object": {
|
|
1099
|
-
const out = {};
|
|
1100
|
-
for (const [k, v] of Object.entries(schema.properties ?? {})) {
|
|
1101
|
-
out[k] = defaultForSchema(v);
|
|
1102
|
-
}
|
|
1103
|
-
return out;
|
|
1104
|
-
}
|
|
1105
|
-
case "array":
|
|
1106
|
-
return [];
|
|
1107
|
-
case "integer":
|
|
1108
|
-
case "number":
|
|
1109
|
-
return 0;
|
|
1110
|
-
case "boolean":
|
|
1111
|
-
return false;
|
|
1112
|
-
case "string":
|
|
1113
|
-
return "";
|
|
1114
|
-
default:
|
|
1115
|
-
if (schema.properties) {
|
|
1116
|
-
const out = {};
|
|
1117
|
-
for (const [k, v] of Object.entries(schema.properties)) {
|
|
1118
|
-
out[k] = defaultForSchema(v);
|
|
1119
|
-
}
|
|
1120
|
-
return out;
|
|
1121
|
-
}
|
|
1122
|
-
return "";
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
__name(defaultForSchema, "defaultForSchema");
|
|
1126
|
-
function BodyFormEditor({ schema, value, onChange }) {
|
|
1127
|
-
return /* @__PURE__ */ jsx(
|
|
1128
|
-
SchemaField,
|
|
1129
|
-
{
|
|
1130
|
-
schema,
|
|
1131
|
-
value,
|
|
1132
|
-
onChange,
|
|
1133
|
-
depth: 0,
|
|
1134
|
-
required: false
|
|
1135
|
-
}
|
|
1136
|
-
);
|
|
1137
|
-
}
|
|
1138
|
-
__name(BodyFormEditor, "BodyFormEditor");
|
|
1139
|
-
function SchemaField({ schema, value, onChange, depth, required, label }) {
|
|
1140
|
-
if (depth > MAX_DEPTH2) {
|
|
1141
|
-
return /* @__PURE__ */ jsx(RawJsonField, { label, value, onChange });
|
|
1142
|
-
}
|
|
1143
|
-
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
1144
|
-
return /* @__PURE__ */ jsx(EnumField, { schema, value, onChange, label, required });
|
|
1145
|
-
}
|
|
1146
|
-
switch (schema.type) {
|
|
1147
|
-
case "object":
|
|
1148
|
-
return /* @__PURE__ */ jsx(ObjectField, { schema, value, onChange, depth, label });
|
|
1149
|
-
case "array":
|
|
1150
|
-
return /* @__PURE__ */ jsx(ArrayField, { schema, value, onChange, depth, label, required });
|
|
1151
|
-
case "boolean":
|
|
1152
|
-
return /* @__PURE__ */ jsx(BooleanField, { value, onChange, label, schema, required });
|
|
1153
|
-
case "integer":
|
|
1154
|
-
case "number":
|
|
1155
|
-
return /* @__PURE__ */ jsx(NumberField, { value, onChange, label, schema, required });
|
|
1156
|
-
case "string":
|
|
1157
|
-
default:
|
|
1158
|
-
if (!schema.type && schema.properties) {
|
|
1159
|
-
return /* @__PURE__ */ jsx(ObjectField, { schema, value, onChange, depth, label });
|
|
1160
|
-
}
|
|
1161
|
-
return /* @__PURE__ */ jsx(StringField, { value, onChange, label, schema, required });
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
__name(SchemaField, "SchemaField");
|
|
1165
|
-
function FieldHeader({
|
|
1166
|
-
label,
|
|
1167
|
-
type,
|
|
1168
|
-
required,
|
|
1169
|
-
description
|
|
1170
|
-
}) {
|
|
1171
|
-
if (!label) return null;
|
|
1172
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
|
|
1173
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-1.5", children: [
|
|
1174
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono text-[11px] text-foreground/80", children: label }),
|
|
1175
|
-
required && /* @__PURE__ */ jsx("span", { className: "text-[9px] text-destructive font-bold leading-none", children: "*" }),
|
|
1176
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-muted-foreground/50", children: type })
|
|
1177
|
-
] }),
|
|
1178
|
-
description && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground/70 leading-snug", children: description })
|
|
1179
|
-
] });
|
|
1180
|
-
}
|
|
1181
|
-
__name(FieldHeader, "FieldHeader");
|
|
1182
|
-
function StringField({
|
|
1183
|
-
value,
|
|
1184
|
-
onChange,
|
|
1185
|
-
label,
|
|
1186
|
-
schema,
|
|
1187
|
-
required
|
|
1188
|
-
}) {
|
|
1189
|
-
const stringValue = typeof value === "string" ? value : value == null ? "" : String(value);
|
|
1190
|
-
const placeholder = schema.format ? `${schema.type ?? "string"} (${schema.format})` : schema.description || schema.type || "string";
|
|
1191
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1192
|
-
/* @__PURE__ */ jsx(FieldHeader, { label, type: schema.format ? `string (${schema.format})` : "string", required, description: schema.description }),
|
|
1193
|
-
/* @__PURE__ */ jsx(
|
|
1194
|
-
Input,
|
|
1195
|
-
{
|
|
1196
|
-
value: stringValue,
|
|
1197
|
-
onChange: (e) => onChange(e.target.value),
|
|
1198
|
-
placeholder,
|
|
1199
|
-
className: "h-8 text-xs font-mono"
|
|
1200
|
-
}
|
|
1201
|
-
)
|
|
1202
|
-
] });
|
|
1203
|
-
}
|
|
1204
|
-
__name(StringField, "StringField");
|
|
1205
|
-
function NumberField({
|
|
1206
|
-
value,
|
|
1207
|
-
onChange,
|
|
1208
|
-
label,
|
|
1209
|
-
schema,
|
|
1210
|
-
required
|
|
1211
|
-
}) {
|
|
1212
|
-
const raw = value == null ? "" : String(value);
|
|
1213
|
-
const type = schema.type === "integer" ? "integer" : "number";
|
|
1214
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1215
|
-
/* @__PURE__ */ jsx(FieldHeader, { label, type: schema.format ? `${type} (${schema.format})` : type, required, description: schema.description }),
|
|
1216
|
-
/* @__PURE__ */ jsx(
|
|
1217
|
-
Input,
|
|
1218
|
-
{
|
|
1219
|
-
type: "number",
|
|
1220
|
-
value: raw,
|
|
1221
|
-
onChange: (e) => {
|
|
1222
|
-
const v = e.target.value;
|
|
1223
|
-
if (v === "") return onChange(null);
|
|
1224
|
-
const n = schema.type === "integer" ? parseInt(v, 10) : parseFloat(v);
|
|
1225
|
-
onChange(Number.isNaN(n) ? null : n);
|
|
1226
|
-
},
|
|
1227
|
-
placeholder: type,
|
|
1228
|
-
className: "h-8 text-xs font-mono"
|
|
1229
|
-
}
|
|
1230
|
-
)
|
|
1231
|
-
] });
|
|
1232
|
-
}
|
|
1233
|
-
__name(NumberField, "NumberField");
|
|
1234
|
-
function BooleanField({
|
|
1235
|
-
value,
|
|
1236
|
-
onChange,
|
|
1237
|
-
label,
|
|
1238
|
-
schema,
|
|
1239
|
-
required
|
|
1240
|
-
}) {
|
|
1241
|
-
const bool = value === true;
|
|
1242
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
1243
|
-
/* @__PURE__ */ jsx(FieldHeader, { label, type: "boolean", required, description: schema.description }),
|
|
1244
|
-
/* @__PURE__ */ jsx(Switch, { checked: bool, onCheckedChange: onChange, className: "mt-0.5 shrink-0" })
|
|
1245
|
-
] });
|
|
1246
|
-
}
|
|
1247
|
-
__name(BooleanField, "BooleanField");
|
|
1248
|
-
function EnumField({
|
|
1249
|
-
schema,
|
|
1250
|
-
value,
|
|
1251
|
-
onChange,
|
|
1252
|
-
label,
|
|
1253
|
-
required
|
|
1254
|
-
}) {
|
|
1255
|
-
const options = (schema.enum ?? []).map((v) => ({
|
|
1256
|
-
value: String(v),
|
|
1257
|
-
label: String(v)
|
|
1258
|
-
}));
|
|
1259
|
-
const strValue = value == null ? "" : String(value);
|
|
1260
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1261
|
-
/* @__PURE__ */ jsx(FieldHeader, { label, type: `${schema.type ?? "enum"} enum`, required, description: schema.description }),
|
|
1262
|
-
/* @__PURE__ */ jsx(
|
|
1263
|
-
Combobox,
|
|
1264
|
-
{
|
|
1265
|
-
options,
|
|
1266
|
-
value: strValue,
|
|
1267
|
-
onValueChange: (v) => {
|
|
1268
|
-
if (schema.type === "integer") onChange(parseInt(v, 10));
|
|
1269
|
-
else if (schema.type === "number") onChange(parseFloat(v));
|
|
1270
|
-
else onChange(v);
|
|
1271
|
-
},
|
|
1272
|
-
placeholder: "Select\u2026",
|
|
1273
|
-
searchPlaceholder: "Search\u2026",
|
|
1274
|
-
className: "h-8 text-xs"
|
|
1275
|
-
}
|
|
1276
|
-
)
|
|
1277
|
-
] });
|
|
1278
|
-
}
|
|
1279
|
-
__name(EnumField, "EnumField");
|
|
1280
|
-
function RawJsonField({
|
|
1281
|
-
label,
|
|
1282
|
-
value,
|
|
1283
|
-
onChange
|
|
1284
|
-
}) {
|
|
1285
|
-
const [text, setText] = React6.useState(() => JSON.stringify(value ?? null, null, 2));
|
|
1286
|
-
React6.useEffect(() => {
|
|
1287
|
-
setText(JSON.stringify(value ?? null, null, 2));
|
|
1288
|
-
}, [value]);
|
|
1289
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1290
|
-
label && /* @__PURE__ */ jsxs(SectionLabel, { children: [
|
|
1291
|
-
label,
|
|
1292
|
-
" (raw)"
|
|
1293
|
-
] }),
|
|
1294
|
-
/* @__PURE__ */ jsx(
|
|
1295
|
-
Textarea,
|
|
1296
|
-
{
|
|
1297
|
-
value: text,
|
|
1298
|
-
onChange: (e) => {
|
|
1299
|
-
setText(e.target.value);
|
|
1300
|
-
try {
|
|
1301
|
-
onChange(JSON.parse(e.target.value));
|
|
1302
|
-
} catch {
|
|
1303
|
-
}
|
|
1304
|
-
},
|
|
1305
|
-
className: "font-mono text-[11px] min-h-[80px]",
|
|
1306
|
-
rows: 4
|
|
1307
|
-
}
|
|
1308
|
-
)
|
|
1309
|
-
] });
|
|
1310
|
-
}
|
|
1311
|
-
__name(RawJsonField, "RawJsonField");
|
|
1312
|
-
function ObjectField({
|
|
1313
|
-
schema,
|
|
1314
|
-
value,
|
|
1315
|
-
onChange,
|
|
1316
|
-
depth,
|
|
1317
|
-
label
|
|
1318
|
-
}) {
|
|
1319
|
-
const obj = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1320
|
-
const required = new Set(schema.required ?? []);
|
|
1321
|
-
const entries = Object.entries(schema.properties ?? {});
|
|
1322
|
-
const setKey = useCallback(
|
|
1323
|
-
(key) => (next) => {
|
|
1324
|
-
onChange({ ...obj, [key]: next });
|
|
1325
|
-
},
|
|
1326
|
-
[obj, onChange]
|
|
1327
|
-
);
|
|
1328
|
-
if (entries.length === 0) {
|
|
1329
|
-
return /* @__PURE__ */ jsx(RawJsonField, { label, value: obj, onChange });
|
|
1330
|
-
}
|
|
1331
|
-
const wrapperClass = depth === 0 ? "space-y-3" : "space-y-2 border-l-2 border-border/60 pl-3 ml-px";
|
|
1332
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1333
|
-
label && depth > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-1.5", children: [
|
|
1334
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono text-[11px] text-foreground/80", children: label }),
|
|
1335
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-muted-foreground/50", children: "object" })
|
|
1336
|
-
] }),
|
|
1337
|
-
/* @__PURE__ */ jsx("div", { className: cn(wrapperClass), children: entries.map(([key, subSchema]) => /* @__PURE__ */ jsx(
|
|
1338
|
-
SchemaField,
|
|
1339
|
-
{
|
|
1340
|
-
schema: subSchema,
|
|
1341
|
-
value: obj[key],
|
|
1342
|
-
onChange: setKey(key),
|
|
1343
|
-
depth: depth + 1,
|
|
1344
|
-
required: required.has(key),
|
|
1345
|
-
label: key
|
|
1346
|
-
},
|
|
1347
|
-
key
|
|
1348
|
-
)) })
|
|
1349
|
-
] });
|
|
1350
|
-
}
|
|
1351
|
-
__name(ObjectField, "ObjectField");
|
|
1352
|
-
function ArrayField({
|
|
1353
|
-
schema,
|
|
1354
|
-
value,
|
|
1355
|
-
onChange,
|
|
1356
|
-
depth,
|
|
1357
|
-
label,
|
|
1358
|
-
required
|
|
1359
|
-
}) {
|
|
1360
|
-
const arr = Array.isArray(value) ? value : [];
|
|
1361
|
-
const items = schema.items ?? { type: "string" };
|
|
1362
|
-
const addItem = /* @__PURE__ */ __name(() => onChange([...arr, defaultForSchema(items)]), "addItem");
|
|
1363
|
-
const removeAt = /* @__PURE__ */ __name((i) => onChange(arr.filter((_, idx) => idx !== i)), "removeAt");
|
|
1364
|
-
const setAt = /* @__PURE__ */ __name((i) => (next) => onChange(arr.map((v, idx) => idx === i ? next : v)), "setAt");
|
|
1365
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1366
|
-
label && /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-1.5", children: [
|
|
1367
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono text-[11px] text-foreground/80", children: label }),
|
|
1368
|
-
required && /* @__PURE__ */ jsx("span", { className: "text-[9px] text-destructive font-bold leading-none", children: "*" }),
|
|
1369
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-muted-foreground/50", children: `array<${items.type ?? "any"}>` })
|
|
1370
|
-
] }),
|
|
1371
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-2 border-l-2 border-border/60 pl-3 ml-px", children: [
|
|
1372
|
-
arr.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground/50 italic", children: "Empty array" }),
|
|
1373
|
-
arr.map((v, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
1374
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(
|
|
1375
|
-
SchemaField,
|
|
1376
|
-
{
|
|
1377
|
-
schema: items,
|
|
1378
|
-
value: v,
|
|
1379
|
-
onChange: setAt(i),
|
|
1380
|
-
depth: depth + 1,
|
|
1381
|
-
required: false,
|
|
1382
|
-
label: `${label ?? ""}[${i}]`
|
|
1383
|
-
}
|
|
1384
|
-
) }),
|
|
1385
|
-
/* @__PURE__ */ jsx(
|
|
1386
|
-
"button",
|
|
1387
|
-
{
|
|
1388
|
-
type: "button",
|
|
1389
|
-
onClick: () => removeAt(i),
|
|
1390
|
-
title: "Remove item",
|
|
1391
|
-
className: "shrink-0 h-7 w-7 inline-flex items-center justify-center rounded-md text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors",
|
|
1392
|
-
children: /* @__PURE__ */ jsx(Minus, { className: "h-3.5 w-3.5" })
|
|
1393
|
-
}
|
|
1394
|
-
)
|
|
1395
|
-
] }, i)),
|
|
1396
|
-
/* @__PURE__ */ jsxs(
|
|
1397
|
-
"button",
|
|
1398
|
-
{
|
|
1399
|
-
type: "button",
|
|
1400
|
-
onClick: addItem,
|
|
1401
|
-
className: "inline-flex items-center gap-1.5 text-[10px] text-muted-foreground hover:text-foreground transition-colors py-1",
|
|
1402
|
-
children: [
|
|
1403
|
-
/* @__PURE__ */ jsx(Plus, { className: "h-3 w-3" }),
|
|
1404
|
-
"Add item"
|
|
1405
|
-
]
|
|
1406
|
-
}
|
|
1407
|
-
)
|
|
1408
|
-
] })
|
|
1409
|
-
] });
|
|
1410
|
-
}
|
|
1411
|
-
__name(ArrayField, "ArrayField");
|
|
1412
|
-
function EndpointResetButton() {
|
|
1413
|
-
const { state, setParameters, setRequestBody } = usePlaygroundContext();
|
|
1414
|
-
const ep = state.selectedEndpoint;
|
|
1415
|
-
const { reset } = useEndpointDraft(state.activeSchemaId, ep);
|
|
1416
|
-
const hasDraft = Object.keys(state.parameters).length > 0 || state.requestBody.length > 0;
|
|
1417
|
-
const onClick = useCallback(() => {
|
|
1418
|
-
setParameters({});
|
|
1419
|
-
setRequestBody("");
|
|
1420
|
-
reset();
|
|
1421
|
-
}, [setParameters, setRequestBody, reset]);
|
|
1422
|
-
if (!ep || !hasDraft) return null;
|
|
1423
|
-
return /* @__PURE__ */ jsxs(
|
|
1424
|
-
"button",
|
|
1425
|
-
{
|
|
1426
|
-
type: "button",
|
|
1427
|
-
onClick,
|
|
1428
|
-
title: "Reset parameters & body (keeps auth)",
|
|
1429
|
-
className: cn(
|
|
1430
|
-
"inline-flex items-center gap-1 text-[10px] text-muted-foreground",
|
|
1431
|
-
"hover:text-foreground transition-colors"
|
|
1432
|
-
),
|
|
1433
|
-
children: [
|
|
1434
|
-
/* @__PURE__ */ jsx(RotateCcw, { className: "h-2.5 w-2.5" }),
|
|
1435
|
-
"Reset"
|
|
1436
|
-
]
|
|
1437
|
-
}
|
|
1438
|
-
);
|
|
1439
|
-
}
|
|
1440
|
-
__name(EndpointResetButton, "EndpointResetButton");
|
|
1441
|
-
function ParamFields({ label, params }) {
|
|
1442
|
-
const { state, setParameters } = usePlaygroundContext();
|
|
1443
|
-
function handleChange(name, value) {
|
|
1444
|
-
setParameters({ ...state.parameters, [name]: value });
|
|
1445
|
-
}
|
|
1446
|
-
__name(handleChange, "handleChange");
|
|
1447
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1448
|
-
/* @__PURE__ */ jsx(SectionLabel, { children: label }),
|
|
1449
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: params.map((p) => {
|
|
1450
|
-
const value = state.parameters[p.name] ?? "";
|
|
1451
|
-
const placeholder = p.description || p.name;
|
|
1452
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1453
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
1454
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono text-[11px] text-foreground/80", children: p.name }),
|
|
1455
|
-
p.required && /* @__PURE__ */ jsx("span", { className: "text-[9px] text-destructive font-bold leading-none", children: "*" }),
|
|
1456
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-muted-foreground/50", children: p.type })
|
|
1457
|
-
] }),
|
|
1458
|
-
/* @__PURE__ */ jsx(
|
|
1459
|
-
Input,
|
|
1460
|
-
{
|
|
1461
|
-
value,
|
|
1462
|
-
onChange: (e) => handleChange(p.name, e.target.value),
|
|
1463
|
-
placeholder,
|
|
1464
|
-
className: "h-8 text-xs font-mono"
|
|
1465
|
-
}
|
|
1466
|
-
)
|
|
1467
|
-
] }, p.name);
|
|
1468
|
-
}) })
|
|
1469
|
-
] });
|
|
1470
|
-
}
|
|
1471
|
-
__name(ParamFields, "ParamFields");
|
|
1472
|
-
function RequestPanel() {
|
|
1473
|
-
const {
|
|
1474
|
-
state,
|
|
1475
|
-
apiKeys,
|
|
1476
|
-
apiKeysLoading,
|
|
1477
|
-
setRequestBody,
|
|
1478
|
-
setRequestHeaders,
|
|
1479
|
-
setSelectedApiKey,
|
|
1480
|
-
setManualApiToken,
|
|
1481
|
-
sendRequest
|
|
1482
|
-
} = usePlaygroundContext();
|
|
1483
|
-
const apiKeyOptions = useMemo(
|
|
1484
|
-
() => apiKeys.map((k) => ({
|
|
1485
|
-
value: k.id,
|
|
1486
|
-
label: k.name || "Unnamed key",
|
|
1487
|
-
// Surface the first 8 chars of the secret so the user
|
|
1488
|
-
// can tell two similarly-named keys apart at a glance.
|
|
1489
|
-
description: k.secret ? `${k.secret.slice(0, 8)}\u2026` : void 0
|
|
1490
|
-
})),
|
|
1491
|
-
[apiKeys]
|
|
1492
|
-
);
|
|
1493
|
-
const hasApiKeys = apiKeyOptions.length > 0;
|
|
1494
|
-
const ep = state.selectedEndpoint;
|
|
1495
|
-
const isJsonValid = state.requestBody ? isValidJson(state.requestBody) : true;
|
|
1496
|
-
const curlCommand = useMemo(() => {
|
|
1497
|
-
if (!state.requestUrl) return "";
|
|
1498
|
-
const absoluteUrl = resolveAbsolute(state.requestUrl);
|
|
1499
|
-
const apiKey = state.selectedApiKey ? findApiKeyById(apiKeys, state.selectedApiKey) : null;
|
|
1500
|
-
const hdrs = parseRequestHeaders(state.requestHeaders);
|
|
1501
|
-
if (apiKey) hdrs["X-API-Key"] = apiKey.secret || apiKey.id;
|
|
1502
|
-
let cmd = `curl -X ${state.requestMethod} "${absoluteUrl}"`;
|
|
1503
|
-
Object.entries(hdrs).forEach(([k, v]) => {
|
|
1504
|
-
cmd += ` \\
|
|
1505
|
-
-H "${k}: ${v}"`;
|
|
1506
|
-
});
|
|
1507
|
-
if (state.requestBody && state.requestMethod !== "GET" && isJsonValid) {
|
|
1508
|
-
cmd += ` \\
|
|
1509
|
-
-d '${state.requestBody}'`;
|
|
1510
|
-
}
|
|
1511
|
-
return cmd;
|
|
1512
|
-
}, [state, apiKeys, isJsonValid]);
|
|
1513
|
-
const pathParams = useMemo(
|
|
1514
|
-
() => ep?.parameters?.filter((p) => ep.path.includes(`{${p.name}}`)) ?? [],
|
|
1515
|
-
[ep]
|
|
1516
|
-
);
|
|
1517
|
-
const queryParams = useMemo(
|
|
1518
|
-
() => ep?.parameters?.filter((p) => !ep.path.includes(`{${p.name}}`)) ?? [],
|
|
1519
|
-
[ep]
|
|
1520
|
-
);
|
|
1521
|
-
state.loading || !state.requestUrl || !isJsonValid;
|
|
1522
|
-
const displayUrl = resolveAbsolute(state.requestUrl || ep?.path || "");
|
|
1523
|
-
const hasBody = ep?.method !== "GET";
|
|
1524
|
-
const bodyType = ep?.requestBody?.type ?? "";
|
|
1525
|
-
const hasPathParams = pathParams.length > 0;
|
|
1526
|
-
const hasQueryParams = queryParams.length > 0;
|
|
1527
|
-
const hasCurl = Boolean(curlCommand);
|
|
1528
|
-
const epPath = ep ? relativePath(ep.path) : "";
|
|
1529
|
-
const urlChanged = displayUrl !== "" && displayUrl !== epPath;
|
|
1530
|
-
if (!ep) {
|
|
1531
|
-
return /* @__PURE__ */ jsx(EmptyState, { icon: Send, text: "Select an endpoint to build a request" });
|
|
1532
|
-
}
|
|
1533
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1534
|
-
(urlChanged || ep) && /* @__PURE__ */ jsxs("div", { className: "shrink-0 border-b px-4 py-2 bg-muted/10 flex items-center gap-2 min-h-[28px]", children: [
|
|
1535
|
-
urlChanged ? /* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-muted-foreground/60 break-all leading-snug truncate min-w-0 flex-1", children: displayUrl }) : /* @__PURE__ */ jsx("span", { className: "flex-1" }),
|
|
1536
|
-
/* @__PURE__ */ jsx(EndpointResetButton, {})
|
|
1537
|
-
] }),
|
|
1538
|
-
/* @__PURE__ */ jsxs(ScrollArea, { className: "px-4 py-3 space-y-3", children: [
|
|
1539
|
-
hasPathParams && /* @__PURE__ */ jsx(ParamFields, { label: "Path Parameters", params: pathParams }),
|
|
1540
|
-
hasQueryParams && /* @__PURE__ */ jsx(ParamFields, { label: "Query Parameters", params: queryParams }),
|
|
1541
|
-
hasBody && /* @__PURE__ */ jsx(
|
|
1542
|
-
BodySection,
|
|
1543
|
-
{
|
|
1544
|
-
schema: ep.requestBody?.schema,
|
|
1545
|
-
bodyType,
|
|
1546
|
-
bodyDescription: ep.requestBody?.description,
|
|
1547
|
-
value: state.requestBody,
|
|
1548
|
-
onChange: setRequestBody,
|
|
1549
|
-
isJsonValid
|
|
1550
|
-
}
|
|
1551
|
-
),
|
|
1552
|
-
/* @__PURE__ */ jsx(
|
|
1553
|
-
CollapsibleSection,
|
|
1554
|
-
{
|
|
1555
|
-
label: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
1556
|
-
/* @__PURE__ */ jsx(Key, { className: "h-2.5 w-2.5" }),
|
|
1557
|
-
"Auth & Headers"
|
|
1558
|
-
] }),
|
|
1559
|
-
children: /* @__PURE__ */ jsxs("div", { className: "space-y-3 pt-2", children: [
|
|
1560
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1561
|
-
/* @__PURE__ */ jsx(SectionLabel, { children: "API Key" }),
|
|
1562
|
-
/* @__PURE__ */ jsx(
|
|
1563
|
-
Combobox,
|
|
1564
|
-
{
|
|
1565
|
-
options: apiKeyOptions,
|
|
1566
|
-
value: state.selectedApiKey ?? "",
|
|
1567
|
-
onValueChange: (v) => setSelectedApiKey(v || null),
|
|
1568
|
-
placeholder: apiKeysLoading ? "Loading keys\u2026" : hasApiKeys ? "Select an API key" : "No API keys yet",
|
|
1569
|
-
searchPlaceholder: "Search keys\u2026",
|
|
1570
|
-
emptyText: "No matching key",
|
|
1571
|
-
disabled: apiKeysLoading || !hasApiKeys,
|
|
1572
|
-
className: "h-8"
|
|
1573
|
-
}
|
|
1574
|
-
),
|
|
1575
|
-
/* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
|
|
1576
|
-
"Picks are sent via the",
|
|
1577
|
-
" ",
|
|
1578
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono", children: "X-API-Key" }),
|
|
1579
|
-
" header."
|
|
1580
|
-
] })
|
|
1581
|
-
] }),
|
|
1582
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1583
|
-
/* @__PURE__ */ jsx(SectionLabel, { children: "Bearer Token" }),
|
|
1584
|
-
/* @__PURE__ */ jsx(
|
|
1585
|
-
Input,
|
|
1586
|
-
{
|
|
1587
|
-
type: "password",
|
|
1588
|
-
placeholder: "Leave empty to use JWT from localStorage",
|
|
1589
|
-
value: state.manualApiToken,
|
|
1590
|
-
onChange: (e) => setManualApiToken(e.target.value),
|
|
1591
|
-
className: "font-mono text-xs h-8"
|
|
1592
|
-
}
|
|
1593
|
-
)
|
|
1594
|
-
] }),
|
|
1595
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1596
|
-
/* @__PURE__ */ jsx(SectionLabel, { children: "Headers" }),
|
|
1597
|
-
/* @__PURE__ */ jsx(
|
|
1598
|
-
Textarea,
|
|
1599
|
-
{
|
|
1600
|
-
value: state.requestHeaders,
|
|
1601
|
-
onChange: (e) => setRequestHeaders(e.target.value),
|
|
1602
|
-
className: "font-mono text-[11px] min-h-[60px] resize-y",
|
|
1603
|
-
rows: 3
|
|
1604
|
-
}
|
|
1605
|
-
)
|
|
1606
|
-
] })
|
|
1607
|
-
] })
|
|
1608
|
-
}
|
|
1609
|
-
),
|
|
1610
|
-
hasCurl && /* @__PURE__ */ jsx(
|
|
1611
|
-
CollapsibleSection,
|
|
1612
|
-
{
|
|
1613
|
-
label: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
1614
|
-
/* @__PURE__ */ jsx(Terminal, { className: "h-2.5 w-2.5" }),
|
|
1615
|
-
"cURL"
|
|
1616
|
-
] }),
|
|
1617
|
-
children: /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(
|
|
1618
|
-
PrettyCode_default,
|
|
1619
|
-
{
|
|
1620
|
-
data: curlCommand,
|
|
1621
|
-
language: "bash",
|
|
1622
|
-
isCompact: true,
|
|
1623
|
-
maxLines: 50
|
|
1624
|
-
}
|
|
1625
|
-
) })
|
|
1626
|
-
}
|
|
1627
|
-
),
|
|
1628
|
-
/* @__PURE__ */ jsx("div", { className: "h-4" })
|
|
1629
|
-
] })
|
|
1630
|
-
] });
|
|
1631
|
-
}
|
|
1632
|
-
__name(RequestPanel, "RequestPanel");
|
|
1633
|
-
function BodySection({ schema, bodyType, bodyDescription, value, onChange, isJsonValid }) {
|
|
1634
|
-
const hasSchema = !!schema;
|
|
1635
|
-
const [mode, setMode] = React6.useState(hasSchema ? "form" : "json");
|
|
1636
|
-
const parsed = React6.useMemo(() => {
|
|
1637
|
-
if (!value) return null;
|
|
1638
|
-
try {
|
|
1639
|
-
return JSON.parse(value);
|
|
1640
|
-
} catch {
|
|
1641
|
-
return null;
|
|
1642
|
-
}
|
|
1643
|
-
}, [value]);
|
|
1644
|
-
const handleFormChange = useCallback(
|
|
1645
|
-
(next) => {
|
|
1646
|
-
onChange(JSON.stringify(next, null, 2));
|
|
1647
|
-
},
|
|
1648
|
-
[onChange]
|
|
1649
|
-
);
|
|
1650
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1651
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
|
|
1652
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2 min-w-0", children: [
|
|
1653
|
-
/* @__PURE__ */ jsx(SectionLabel, { children: "Body" }),
|
|
1654
|
-
bodyType && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground/40 font-mono", children: bodyType }),
|
|
1655
|
-
bodyDescription && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground/60 truncate", children: bodyDescription })
|
|
1656
|
-
] }),
|
|
1657
|
-
hasSchema && /* @__PURE__ */ jsxs("div", { className: "inline-flex rounded-md border overflow-hidden text-[10px]", children: [
|
|
1658
|
-
/* @__PURE__ */ jsx(ModeButton, { active: mode === "form", onClick: () => setMode("form"), children: "Form" }),
|
|
1659
|
-
/* @__PURE__ */ jsx(ModeButton, { active: mode === "json", onClick: () => setMode("json"), children: "JSON" })
|
|
1660
|
-
] }),
|
|
1661
|
-
mode === "json" && isJsonValid && value && /* @__PURE__ */ jsxs(
|
|
1662
|
-
"button",
|
|
1663
|
-
{
|
|
1664
|
-
type: "button",
|
|
1665
|
-
onClick: () => {
|
|
1666
|
-
try {
|
|
1667
|
-
onChange(JSON.stringify(JSON.parse(value), null, 2));
|
|
1668
|
-
} catch {
|
|
1669
|
-
}
|
|
1670
|
-
},
|
|
1671
|
-
className: "inline-flex items-center gap-1 text-[10px] text-muted-foreground hover:text-foreground transition-colors",
|
|
1672
|
-
children: [
|
|
1673
|
-
/* @__PURE__ */ jsx(Sparkles, { className: "h-2.5 w-2.5" }),
|
|
1674
|
-
"Format"
|
|
1675
|
-
]
|
|
1676
|
-
}
|
|
1677
|
-
)
|
|
1678
|
-
] }),
|
|
1679
|
-
mode === "form" && hasSchema ? /* @__PURE__ */ jsx(
|
|
1680
|
-
BodyFormEditor,
|
|
1681
|
-
{
|
|
1682
|
-
schema,
|
|
1683
|
-
value: parsed,
|
|
1684
|
-
onChange: handleFormChange
|
|
1685
|
-
}
|
|
1686
|
-
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1687
|
-
/* @__PURE__ */ jsx(
|
|
1688
|
-
Textarea,
|
|
1689
|
-
{
|
|
1690
|
-
placeholder: '{\n "key": "value"\n}',
|
|
1691
|
-
value,
|
|
1692
|
-
onChange: (e) => onChange(e.target.value),
|
|
1693
|
-
className: cn(
|
|
1694
|
-
"font-mono text-[11px] min-h-[90px] resize-y",
|
|
1695
|
-
!isJsonValid && "border-destructive focus-visible:ring-destructive/30"
|
|
1696
|
-
),
|
|
1697
|
-
rows: 4
|
|
1698
|
-
}
|
|
1699
|
-
),
|
|
1700
|
-
!isJsonValid && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-destructive", children: "Invalid JSON" })
|
|
1701
|
-
] })
|
|
1702
|
-
] });
|
|
1703
|
-
}
|
|
1704
|
-
__name(BodySection, "BodySection");
|
|
1705
|
-
function ModeButton({
|
|
1706
|
-
active,
|
|
1707
|
-
onClick,
|
|
1708
|
-
children
|
|
1709
|
-
}) {
|
|
1710
|
-
return /* @__PURE__ */ jsx(
|
|
1711
|
-
"button",
|
|
1712
|
-
{
|
|
1713
|
-
type: "button",
|
|
1714
|
-
onClick,
|
|
1715
|
-
className: cn(
|
|
1716
|
-
"px-2 py-0.5 font-medium transition-colors",
|
|
1717
|
-
active ? "bg-primary/10 text-foreground" : "text-muted-foreground hover:text-foreground"
|
|
1718
|
-
),
|
|
1719
|
-
children
|
|
1720
|
-
}
|
|
1721
|
-
);
|
|
1722
|
-
}
|
|
1723
|
-
__name(ModeButton, "ModeButton");
|
|
1724
|
-
var JSON_TREE_CONFIG = {
|
|
1725
|
-
maxAutoExpandDepth: 2,
|
|
1726
|
-
maxAutoExpandArrayItems: 10,
|
|
1727
|
-
maxAutoExpandObjectKeys: 5,
|
|
1728
|
-
maxStringLength: 200,
|
|
1729
|
-
collectionLimit: 50,
|
|
1730
|
-
showCollectionInfo: true,
|
|
1731
|
-
showExpandControls: true,
|
|
1732
|
-
showActionButtons: false,
|
|
1733
|
-
preserveKeyOrder: true,
|
|
1734
|
-
className: "border-0 rounded-none"
|
|
1735
|
-
};
|
|
1736
|
-
function ResponsePanel() {
|
|
1737
|
-
const { state } = usePlaygroundContext();
|
|
1738
|
-
const { response, loading, selectedEndpoint } = state;
|
|
1739
|
-
const { treeData, rawText } = useMemo(() => {
|
|
1740
|
-
const d = response?.data;
|
|
1741
|
-
if (d == null) return { treeData: null, rawText: "" };
|
|
1742
|
-
if (typeof d === "string") {
|
|
1743
|
-
try {
|
|
1744
|
-
return { treeData: JSON.parse(d), rawText: d };
|
|
1745
|
-
} catch {
|
|
1746
|
-
return { treeData: null, rawText: d };
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
return { treeData: d, rawText: JSON.stringify(d, null, 2) };
|
|
1750
|
-
}, [response?.data]);
|
|
1751
|
-
const sizeKb = rawText ? `${(rawText.length / 1024).toFixed(1)} KB` : "";
|
|
1752
|
-
const duration = response?.duration != null ? `${response.duration}ms` : "";
|
|
1753
|
-
const hasError = Boolean(response?.error);
|
|
1754
|
-
const hasStatus = response?.status != null;
|
|
1755
|
-
const hasCopy = Boolean(rawText);
|
|
1756
|
-
if (loading) {
|
|
1757
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center h-full gap-2", children: [
|
|
1758
|
-
/* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }),
|
|
1759
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Sending\u2026" })
|
|
1760
|
-
] });
|
|
1761
|
-
}
|
|
1762
|
-
if (!selectedEndpoint) return /* @__PURE__ */ jsx(EmptyState, { icon: Terminal, text: "Response will appear here" });
|
|
1763
|
-
if (!response) return /* @__PURE__ */ jsx(EmptyState, { icon: Send, text: 'Press "Send Request" to see the response' });
|
|
1764
|
-
if (hasError && !hasStatus) {
|
|
1765
|
-
return /* @__PURE__ */ jsx(
|
|
1766
|
-
EmptyState,
|
|
1767
|
-
{
|
|
1768
|
-
icon: WifiOff,
|
|
1769
|
-
text: response.error,
|
|
1770
|
-
className: "text-destructive [&_svg]:text-destructive"
|
|
1771
|
-
}
|
|
1772
|
-
);
|
|
1773
|
-
}
|
|
1774
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1775
|
-
/* @__PURE__ */ jsxs("div", { className: "shrink-0 border-b px-4 py-2 flex items-center justify-between gap-3 bg-muted/20", children: [
|
|
1776
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
1777
|
-
hasStatus && /* @__PURE__ */ jsx(StatusBadge, { status: response.status }),
|
|
1778
|
-
response.statusText && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground truncate", children: response.statusText }),
|
|
1779
|
-
sizeKb && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground/50 tabular-nums shrink-0", children: sizeKb }),
|
|
1780
|
-
duration && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground/50 tabular-nums shrink-0", children: duration })
|
|
1781
|
-
] }),
|
|
1782
|
-
hasCopy && /* @__PURE__ */ jsx(CopyButton, { value: rawText, variant: "ghost", size: "sm", className: "h-6 px-2 text-[10px] text-muted-foreground shrink-0", children: "Copy" })
|
|
1783
|
-
] }),
|
|
1784
|
-
hasError && /* @__PURE__ */ jsx("div", { className: "shrink-0 mx-4 mt-3 rounded border border-destructive/20 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: response.error }) }),
|
|
1785
|
-
/* @__PURE__ */ jsx(ScrollArea, { children: treeData != null ? /* @__PURE__ */ jsx(JsonTree_default, { title: "Response Body", data: treeData, config: JSON_TREE_CONFIG }) : rawText ? /* @__PURE__ */ jsx("pre", { className: "p-4 text-[11px] font-mono text-foreground/70 whitespace-pre-wrap break-all leading-relaxed", children: rawText }) : /* @__PURE__ */ jsx("div", { className: "py-10 text-center text-xs text-muted-foreground", children: "Empty response body" }) })
|
|
1786
|
-
] });
|
|
1787
|
-
}
|
|
1788
|
-
__name(ResponsePanel, "ResponsePanel");
|
|
1789
|
-
function SendButton({ className }) {
|
|
1790
|
-
const { state, sendRequest } = usePlaygroundContext();
|
|
1791
|
-
const ep = state.selectedEndpoint;
|
|
1792
|
-
const builder = useMemo(
|
|
1793
|
-
() => ep ? new UrlBuilder(ep, state.parameters) : null,
|
|
1794
|
-
[ep, state.parameters]
|
|
1795
|
-
);
|
|
1796
|
-
const missingRequired = builder?.missingRequired() ?? [];
|
|
1797
|
-
const unsubstituted = builder?.unfilledPlaceholders() ?? [];
|
|
1798
|
-
const isJsonValid = state.requestBody ? isValidJson(state.requestBody) : true;
|
|
1799
|
-
const blockers = [];
|
|
1800
|
-
if (missingRequired.length > 0) {
|
|
1801
|
-
blockers.push(
|
|
1802
|
-
`Fill required parameter${missingRequired.length > 1 ? "s" : ""}: ${missingRequired.join(", ")}`
|
|
1803
|
-
);
|
|
1804
|
-
} else if (unsubstituted.length > 0) {
|
|
1805
|
-
blockers.push(`URL still has unfilled placeholder${unsubstituted.length > 1 ? "s" : ""}: ${unsubstituted.map((n) => `{${n}}`).join(", ")}`);
|
|
1806
|
-
}
|
|
1807
|
-
if (!isJsonValid) blockers.push("Request body is not valid JSON");
|
|
1808
|
-
const disabled = state.loading || !state.requestUrl || blockers.length > 0;
|
|
1809
|
-
const tooltip = blockers.length > 0 ? blockers.join("\n") : void 0;
|
|
1810
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
|
|
1811
|
-
blockers.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 rounded-md border border-amber-500/25 bg-amber-500/[0.06] px-3 py-2 text-[11px] text-amber-600 dark:text-amber-400", children: [
|
|
1812
|
-
/* @__PURE__ */ jsx(AlertCircle, { className: "h-3.5 w-3.5 shrink-0 mt-px" }),
|
|
1813
|
-
/* @__PURE__ */ jsx("span", { className: "leading-snug", children: blockers[0] })
|
|
1814
|
-
] }),
|
|
1815
|
-
/* @__PURE__ */ jsx(
|
|
1816
|
-
Button,
|
|
1817
|
-
{
|
|
1818
|
-
onClick: sendRequest,
|
|
1819
|
-
disabled,
|
|
1820
|
-
size: "sm",
|
|
1821
|
-
title: tooltip,
|
|
1822
|
-
className: "w-full gap-2 h-9",
|
|
1823
|
-
children: state.loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1824
|
-
/* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
|
|
1825
|
-
"Sending\u2026"
|
|
1826
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1827
|
-
/* @__PURE__ */ jsx(Send, { className: "h-3.5 w-3.5" }),
|
|
1828
|
-
"Send Request"
|
|
1829
|
-
] })
|
|
1830
|
-
}
|
|
1831
|
-
)
|
|
1832
|
-
] });
|
|
1833
|
-
}
|
|
1834
|
-
__name(SendButton, "SendButton");
|
|
1835
|
-
var WIDTH_NARROW = "clamp(380px, 30vw, 480px)";
|
|
1836
|
-
var WIDTH_WIDE = "clamp(720px, 60vw, 1280px)";
|
|
1837
|
-
function SlideInPlayground({ open, onClose }) {
|
|
1838
|
-
const { state } = usePlaygroundContext();
|
|
1839
|
-
const ep = state.selectedEndpoint;
|
|
1840
|
-
const showResponse = state.response !== null || state.loading;
|
|
1841
|
-
const width = showResponse ? WIDTH_WIDE : WIDTH_NARROW;
|
|
1842
|
-
return /* @__PURE__ */ jsx(SidePanel, { open, onOpenChange: (v) => !v && onClose(), side: "right", children: /* @__PURE__ */ jsxs(SidePanel.Content, { width, className: "max-w-[95vw]", children: [
|
|
1843
|
-
/* @__PURE__ */ jsxs(SidePanel.Header, { children: [
|
|
1844
|
-
/* @__PURE__ */ jsx(SidePanel.Title, { children: "Playground" }),
|
|
1845
|
-
ep && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [
|
|
1846
|
-
/* @__PURE__ */ jsx(MethodBadge, { method: ep.method }),
|
|
1847
|
-
/* @__PURE__ */ jsx("code", { className: "font-mono text-[11px] text-muted-foreground truncate", children: relativePath(ep.path) })
|
|
1848
|
-
] }),
|
|
1849
|
-
/* @__PURE__ */ jsx(SidePanel.Close, { className: "ml-auto" })
|
|
1850
|
-
] }),
|
|
1851
|
-
/* @__PURE__ */ jsxs(
|
|
1852
|
-
SidePanel.Body,
|
|
1853
|
-
{
|
|
1854
|
-
className: cn(
|
|
1855
|
-
"overflow-hidden grid divide-x transition-[grid-template-columns] duration-250",
|
|
1856
|
-
showResponse ? "grid-cols-[minmax(0,1fr)_minmax(0,1fr)]" : "grid-cols-1"
|
|
1857
|
-
),
|
|
1858
|
-
children: [
|
|
1859
|
-
/* @__PURE__ */ jsx(Panel, { children: /* @__PURE__ */ jsx(RequestPanel, {}) }),
|
|
1860
|
-
showResponse && /* @__PURE__ */ jsx(Panel, { children: /* @__PURE__ */ jsx(ResponsePanel, {}) })
|
|
1861
|
-
]
|
|
1862
|
-
}
|
|
1863
|
-
),
|
|
1864
|
-
ep && /* @__PURE__ */ jsx(SidePanel.Footer, { className: "px-4 py-3", children: /* @__PURE__ */ jsx(SendButton, {}) })
|
|
1865
|
-
] }) });
|
|
1866
|
-
}
|
|
1867
|
-
__name(SlideInPlayground, "SlideInPlayground");
|
|
1868
|
-
function TryItSheet({ open, onOpenChange }) {
|
|
1869
|
-
const { state } = usePlaygroundContext();
|
|
1870
|
-
const showResponse = state.response !== null || state.loading;
|
|
1871
|
-
return /* @__PURE__ */ jsx(ResponsiveSheet, { open, onOpenChange, children: /* @__PURE__ */ jsxs(ResponsiveSheetContent, { className: "sm:max-w-xl flex flex-col h-full p-0", children: [
|
|
1872
|
-
/* @__PURE__ */ jsx(ResponsiveSheetHeader, { className: "px-4 py-3 border-b shrink-0", children: /* @__PURE__ */ jsx(ResponsiveSheetTitle, { className: "text-sm", children: "Playground" }) }),
|
|
1873
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-h-0 flex flex-col divide-y", children: [
|
|
1874
|
-
/* @__PURE__ */ jsx("div", { className: showResponse ? "flex-1 min-h-0 flex flex-col" : "flex-1 min-h-0 flex flex-col", children: /* @__PURE__ */ jsx(RequestPanel, {}) }),
|
|
1875
|
-
showResponse && /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 flex flex-col", children: /* @__PURE__ */ jsx(ResponsePanel, {}) })
|
|
1876
|
-
] }),
|
|
1877
|
-
state.selectedEndpoint && /* @__PURE__ */ jsx("div", { className: "shrink-0 border-t px-4 py-3 bg-background/95 backdrop-blur-sm", children: /* @__PURE__ */ jsx(SendButton, {}) })
|
|
1878
|
-
] }) });
|
|
1879
|
-
}
|
|
1880
|
-
__name(TryItSheet, "TryItSheet");
|
|
1881
|
-
var DocsLayout = /* @__PURE__ */ __name(() => {
|
|
1882
|
-
const { state, config, setSelectedEndpoint } = usePlaygroundContext();
|
|
1883
|
-
const isDesktop = useMediaQuery("(min-width: 1024px)");
|
|
1884
|
-
const isMobile = !isDesktop;
|
|
1885
|
-
const {
|
|
1886
|
-
endpoints,
|
|
1887
|
-
schemaInfo,
|
|
1888
|
-
rawSchema,
|
|
1889
|
-
resolvedBaseUrl,
|
|
1890
|
-
loading,
|
|
1891
|
-
error,
|
|
1892
|
-
schemas,
|
|
1893
|
-
currentSchema,
|
|
1894
|
-
setCurrentSchema
|
|
1895
|
-
} = useOpenApiSchema({
|
|
1896
|
-
schemas: config.schemas,
|
|
1897
|
-
defaultSchemaId: config.defaultSchemaId,
|
|
1898
|
-
baseUrl: config.baseUrl
|
|
1899
|
-
});
|
|
1900
|
-
const [activeAnchor, setActiveAnchor] = useState(null);
|
|
1901
|
-
const [sheetOpen, setSheetOpen] = useState(false);
|
|
1902
|
-
const docsRef = useRef(null);
|
|
1903
|
-
const slideOpen = !isMobile && state.selectedEndpoint !== null;
|
|
1904
|
-
const handleTry = useCallback(
|
|
1905
|
-
(ep) => {
|
|
1906
|
-
setSelectedEndpoint(ep);
|
|
1907
|
-
if (isMobile) setSheetOpen(true);
|
|
1908
|
-
},
|
|
1909
|
-
[isMobile, setSelectedEndpoint]
|
|
1910
|
-
);
|
|
1911
|
-
const handleCloseSlide = useCallback(() => {
|
|
1912
|
-
setSelectedEndpoint(null);
|
|
1913
|
-
}, [setSelectedEndpoint]);
|
|
1914
|
-
const handleNavigate = useCallback((anchor) => {
|
|
1915
|
-
docsRef.current?.scrollToAnchor(anchor);
|
|
1916
|
-
}, []);
|
|
1917
|
-
if (loading) {
|
|
1918
|
-
return /* @__PURE__ */ jsxs(
|
|
1919
|
-
"div",
|
|
1920
|
-
{
|
|
1921
|
-
className: "grid grid-cols-[260px_1fr] min-h-0 overflow-hidden",
|
|
1922
|
-
style: { height: "calc(100dvh - var(--navbar-height, 64px))" },
|
|
1923
|
-
children: [
|
|
1924
|
-
/* @__PURE__ */ jsx("div", { className: "border-r p-3 space-y-1.5", children: Array.from({ length: 12 }).map((_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-full rounded" }, i)) }),
|
|
1925
|
-
/* @__PURE__ */ jsxs("div", { className: "p-8 space-y-4", children: [
|
|
1926
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-1/2" }),
|
|
1927
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" }),
|
|
1928
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-3/4" }),
|
|
1929
|
-
/* @__PURE__ */ jsx("div", { className: "mt-8 space-y-6", children: Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1930
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-1/3" }),
|
|
1931
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-20 w-full" })
|
|
1932
|
-
] }, i)) })
|
|
1933
|
-
] })
|
|
1934
|
-
]
|
|
1935
|
-
}
|
|
1936
|
-
);
|
|
1937
|
-
}
|
|
1938
|
-
if (error) {
|
|
1939
|
-
return /* @__PURE__ */ jsx(
|
|
1940
|
-
"div",
|
|
1941
|
-
{
|
|
1942
|
-
className: "flex items-center justify-center p-8",
|
|
1943
|
-
style: { height: "calc(100dvh - var(--navbar-height, 64px))" },
|
|
1944
|
-
children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-destructive", children: [
|
|
1945
|
-
"Failed to load schema: ",
|
|
1946
|
-
error
|
|
1947
|
-
] })
|
|
1948
|
-
}
|
|
1949
|
-
);
|
|
1950
|
-
}
|
|
1951
|
-
if (isMobile) {
|
|
1952
|
-
return /* @__PURE__ */ jsxs(
|
|
1953
|
-
"div",
|
|
1954
|
-
{
|
|
1955
|
-
className: "flex flex-col overflow-hidden",
|
|
1956
|
-
style: { height: "calc(100dvh - var(--navbar-height, 64px))" },
|
|
1957
|
-
children: [
|
|
1958
|
-
/* @__PURE__ */ jsx(EndpointDraftSync, { schemaId: currentSchema?.id ?? null }),
|
|
1959
|
-
/* @__PURE__ */ jsx(
|
|
1960
|
-
DocsView,
|
|
1961
|
-
{
|
|
1962
|
-
ref: docsRef,
|
|
1963
|
-
info: schemaInfo,
|
|
1964
|
-
rawSchema,
|
|
1965
|
-
resolvedBaseUrl,
|
|
1966
|
-
endpoints,
|
|
1967
|
-
selectedVersion: state.selectedVersion,
|
|
1968
|
-
loadedEndpoint: state.selectedEndpoint,
|
|
1969
|
-
onTryEndpoint: handleTry,
|
|
1970
|
-
onActiveChange: setActiveAnchor
|
|
1971
|
-
}
|
|
1972
|
-
),
|
|
1973
|
-
/* @__PURE__ */ jsx(TryItSheet, { open: sheetOpen, onOpenChange: setSheetOpen })
|
|
1974
|
-
]
|
|
1975
|
-
}
|
|
1976
|
-
);
|
|
1977
|
-
}
|
|
1978
|
-
return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 350, children: /* @__PURE__ */ jsxs(
|
|
1979
|
-
"div",
|
|
1980
|
-
{
|
|
1981
|
-
className: "grid grid-cols-[260px_minmax(0,1fr)] min-h-0 overflow-hidden",
|
|
1982
|
-
style: { height: "calc(100dvh - var(--navbar-height, 64px))" },
|
|
1983
|
-
children: [
|
|
1984
|
-
/* @__PURE__ */ jsx(EndpointDraftSync, { schemaId: currentSchema?.id ?? null }),
|
|
1985
|
-
/* @__PURE__ */ jsx(
|
|
1986
|
-
DocsSidebar,
|
|
1987
|
-
{
|
|
1988
|
-
info: schemaInfo,
|
|
1989
|
-
endpoints,
|
|
1990
|
-
schemas,
|
|
1991
|
-
currentSchemaId: currentSchema?.id ?? null,
|
|
1992
|
-
onSchemaChange: setCurrentSchema,
|
|
1993
|
-
activeEndpointId: activeAnchor,
|
|
1994
|
-
selectedVersion: state.selectedVersion,
|
|
1995
|
-
onNavigate: handleNavigate
|
|
1996
|
-
}
|
|
1997
|
-
),
|
|
1998
|
-
/* @__PURE__ */ jsx(
|
|
1999
|
-
DocsView,
|
|
2000
|
-
{
|
|
2001
|
-
ref: docsRef,
|
|
2002
|
-
info: schemaInfo,
|
|
2003
|
-
rawSchema,
|
|
2004
|
-
resolvedBaseUrl,
|
|
2005
|
-
endpoints,
|
|
2006
|
-
selectedVersion: state.selectedVersion,
|
|
2007
|
-
loadedEndpoint: state.selectedEndpoint,
|
|
2008
|
-
onTryEndpoint: handleTry,
|
|
2009
|
-
onActiveChange: setActiveAnchor
|
|
2010
|
-
}
|
|
2011
|
-
),
|
|
2012
|
-
/* @__PURE__ */ jsx(SlideInPlayground, { open: slideOpen, onClose: handleCloseSlide })
|
|
2013
|
-
]
|
|
2014
|
-
}
|
|
2015
|
-
) });
|
|
2016
|
-
}, "DocsLayout");
|
|
2017
|
-
|
|
2018
|
-
export { DocsLayout };
|
|
2019
|
-
//# sourceMappingURL=DocsLayout-ERETJLLV.mjs.map
|
|
2020
|
-
//# sourceMappingURL=DocsLayout-ERETJLLV.mjs.map
|