@djangocfg/ui-tools 2.1.286 → 2.1.289
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DocsLayout-ERETJLLV.mjs → DocsLayout-TKJQ5W5E.mjs} +848 -266
- package/dist/DocsLayout-TKJQ5W5E.mjs.map +1 -0
- package/dist/{DocsLayout-BCVU6TTX.cjs → DocsLayout-YDR7DSMM.cjs} +843 -261
- package/dist/DocsLayout-YDR7DSMM.cjs.map +1 -0
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.mjs +2 -2
- package/package.json +9 -6
- package/src/tools/OpenapiViewer/.claude/.sidecar/activity.jsonl +2 -0
- package/src/tools/OpenapiViewer/.claude/.sidecar/history/2026-04-22.md +35 -0
- package/src/tools/OpenapiViewer/.claude/.sidecar/review.md +35 -0
- package/src/tools/OpenapiViewer/.claude/.sidecar/scan.log +3 -0
- package/src/tools/OpenapiViewer/.claude/.sidecar/tasks/T-001.md +18 -0
- package/src/tools/OpenapiViewer/.claude/.sidecar/tasks/T-002.md +18 -0
- package/src/tools/OpenapiViewer/.claude/.sidecar/tasks/T-003.md +18 -0
- package/src/tools/OpenapiViewer/.claude/.sidecar/tasks/T-004.md +18 -0
- package/src/tools/OpenapiViewer/.claude/.sidecar/tasks/T-005.md +18 -0
- package/src/tools/OpenapiViewer/.claude/.sidecar/usage.json +2 -2
- package/src/tools/OpenapiViewer/OpenapiViewer.story.tsx +27 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/DocsView.tsx +326 -54
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc.tsx +7 -2
- package/src/tools/OpenapiViewer/components/DocsLayout/SchemaCopyMenu.tsx +32 -9
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar.tsx +348 -120
- 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/hooks/index.ts +3 -1
- package/src/tools/OpenapiViewer/hooks/useDocsUrlSync.ts +119 -0
- package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +127 -7
- package/src/tools/OpenapiViewer/types.ts +36 -1
- package/src/tools/OpenapiViewer/utils/scrollParent.ts +68 -0
- package/dist/DocsLayout-BCVU6TTX.cjs.map +0 -1
- package/dist/DocsLayout-ERETJLLV.mjs.map +0 -1
|
@@ -2,13 +2,14 @@ import { deduplicateEndpoints, dereferenceSchema, resolveBaseUrl, usePlaygroundC
|
|
|
2
2
|
import { JsonTree_default } from './chunk-LFWQ36LJ.mjs';
|
|
3
3
|
import './chunk-SSUOENAZ.mjs';
|
|
4
4
|
import { __name } from './chunk-CGILA3WO.mjs';
|
|
5
|
-
import React6, { useRef,
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import React6, { useRef, useCallback, useEffect, useMemo, useState } from 'react';
|
|
6
|
+
import { groupBy, orderBy, partition, sortBy, keyBy } from 'lodash-es';
|
|
7
|
+
import { Tooltip, TooltipTrigger, TooltipContent, DropdownMenu, DropdownMenuTrigger, Button, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuItem, Combobox, Input, CopyButton, Switch, Textarea, SidePanel, ResponsiveSheet, ResponsiveSheetContent, ResponsiveSheetHeader, ResponsiveSheetTitle, Skeleton, TooltipProvider } from '@djangocfg/ui-core/components';
|
|
8
|
+
import { toast, useMediaQuery } from '@djangocfg/ui-core/hooks';
|
|
8
9
|
import consola from 'consola';
|
|
9
|
-
import { ChevronRight,
|
|
10
|
+
import { ChevronRight, Sparkles, ChevronDown, Check, Search, Link2, Play, Minus, Plus, RotateCcw, Send, Key, Terminal, Loader2, WifiOff, AlertCircle } from 'lucide-react';
|
|
10
11
|
import { cn } from '@djangocfg/ui-core/lib';
|
|
11
|
-
import {
|
|
12
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
12
13
|
|
|
13
14
|
function exampleFromSchema(schema, depth = 0) {
|
|
14
15
|
if (!schema || depth > 8) return null;
|
|
@@ -51,7 +52,7 @@ function exampleFromSchema(schema, depth = 0) {
|
|
|
51
52
|
}
|
|
52
53
|
__name(exampleFromSchema, "exampleFromSchema");
|
|
53
54
|
var HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
|
|
54
|
-
var extractEndpoints = /* @__PURE__ */ __name((schema, baseUrl) => {
|
|
55
|
+
var extractEndpoints = /* @__PURE__ */ __name((schema, baseUrl, schemaId) => {
|
|
55
56
|
const endpoints = [];
|
|
56
57
|
if (!schema.paths) return [];
|
|
57
58
|
for (const [path, methods] of Object.entries(schema.paths)) {
|
|
@@ -102,7 +103,8 @@ var extractEndpoints = /* @__PURE__ */ __name((schema, baseUrl) => {
|
|
|
102
103
|
category,
|
|
103
104
|
parameters: parameters.length > 0 ? parameters : void 0,
|
|
104
105
|
requestBody,
|
|
105
|
-
responses: responses.length > 0 ? responses : void 0
|
|
106
|
+
responses: responses.length > 0 ? responses : void 0,
|
|
107
|
+
schemaId
|
|
106
108
|
};
|
|
107
109
|
endpoints.push(endpoint);
|
|
108
110
|
}
|
|
@@ -128,7 +130,8 @@ var fetchSchema = /* @__PURE__ */ __name(async (url) => {
|
|
|
128
130
|
function useOpenApiSchema({
|
|
129
131
|
schemas,
|
|
130
132
|
defaultSchemaId,
|
|
131
|
-
baseUrl: configBaseUrl
|
|
133
|
+
baseUrl: configBaseUrl,
|
|
134
|
+
preloadAll = false
|
|
132
135
|
}) {
|
|
133
136
|
const [loading, setLoading] = useState(true);
|
|
134
137
|
const [error, setError] = useState(null);
|
|
@@ -138,6 +141,7 @@ function useOpenApiSchema({
|
|
|
138
141
|
const [loadedSchemas, setLoadedSchemas] = useState(
|
|
139
142
|
/* @__PURE__ */ new Map()
|
|
140
143
|
);
|
|
144
|
+
const [loadStates, setLoadStates] = useState(/* @__PURE__ */ new Map());
|
|
141
145
|
const currentSchema = useMemo(
|
|
142
146
|
() => schemas.find((s) => s.id === currentSchemaId) || null,
|
|
143
147
|
[schemas, currentSchemaId]
|
|
@@ -159,8 +163,8 @@ function useOpenApiSchema({
|
|
|
159
163
|
[currentSchema?.baseUrl, configBaseUrl, currentOpenApiSchema]
|
|
160
164
|
);
|
|
161
165
|
const endpoints = useMemo(
|
|
162
|
-
() => dereferencedSchema ? extractEndpoints(dereferencedSchema, resolvedBaseUrl) : [],
|
|
163
|
-
[dereferencedSchema, resolvedBaseUrl]
|
|
166
|
+
() => dereferencedSchema ? extractEndpoints(dereferencedSchema, resolvedBaseUrl, currentSchemaId) : [],
|
|
167
|
+
[dereferencedSchema, resolvedBaseUrl, currentSchemaId]
|
|
164
168
|
);
|
|
165
169
|
const categories = useMemo(() => getCategories(endpoints), [endpoints]);
|
|
166
170
|
const schemaInfo = useMemo(() => {
|
|
@@ -174,6 +178,7 @@ function useOpenApiSchema({
|
|
|
174
178
|
};
|
|
175
179
|
}, [currentOpenApiSchema]);
|
|
176
180
|
useEffect(() => {
|
|
181
|
+
if (preloadAll) return;
|
|
177
182
|
if (!currentSchema) return;
|
|
178
183
|
if (loadedSchemas.has(currentSchema.id)) {
|
|
179
184
|
setLoading(false);
|
|
@@ -190,7 +195,90 @@ function useOpenApiSchema({
|
|
|
190
195
|
setError(err instanceof Error ? err.message : "Failed to load schema");
|
|
191
196
|
setLoading(false);
|
|
192
197
|
});
|
|
193
|
-
}, [currentSchema, loadedSchemas]);
|
|
198
|
+
}, [currentSchema, loadedSchemas, preloadAll]);
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
if (!preloadAll) return;
|
|
201
|
+
if (schemas.length === 0) {
|
|
202
|
+
setLoading(false);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
let cancelled = false;
|
|
206
|
+
const pending = schemas.filter((s) => !loadedSchemas.has(s.id));
|
|
207
|
+
if (pending.length === 0) {
|
|
208
|
+
setLoading(false);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
setLoading(true);
|
|
212
|
+
setLoadStates((prev) => {
|
|
213
|
+
const next = new Map(prev);
|
|
214
|
+
for (const s of pending) next.set(s.id, { loading: true, error: null });
|
|
215
|
+
return next;
|
|
216
|
+
});
|
|
217
|
+
Promise.allSettled(
|
|
218
|
+
pending.map(
|
|
219
|
+
(s) => fetchSchema(s.url).then((schema) => ({ id: s.id, name: s.name, schema }))
|
|
220
|
+
)
|
|
221
|
+
).then((results) => {
|
|
222
|
+
if (cancelled) return;
|
|
223
|
+
setLoadedSchemas((prev) => {
|
|
224
|
+
const next = new Map(prev);
|
|
225
|
+
for (const r of results) {
|
|
226
|
+
if (r.status === "fulfilled") {
|
|
227
|
+
next.set(r.value.id, r.value.schema);
|
|
228
|
+
consola.success(`Schema loaded: ${r.value.name}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return next;
|
|
232
|
+
});
|
|
233
|
+
setLoadStates((prev) => {
|
|
234
|
+
const next = new Map(prev);
|
|
235
|
+
results.forEach((r, i) => {
|
|
236
|
+
const src = pending[i];
|
|
237
|
+
if (r.status === "fulfilled") {
|
|
238
|
+
next.set(src.id, { loading: false, error: null });
|
|
239
|
+
} else {
|
|
240
|
+
const msg = r.reason instanceof Error ? r.reason.message : "Failed to load schema";
|
|
241
|
+
consola.error(`Error loading schema from ${src.url}:`, r.reason);
|
|
242
|
+
next.set(src.id, { loading: false, error: msg });
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
return next;
|
|
246
|
+
});
|
|
247
|
+
setLoading(false);
|
|
248
|
+
});
|
|
249
|
+
return () => {
|
|
250
|
+
cancelled = true;
|
|
251
|
+
};
|
|
252
|
+
}, [preloadAll, schemas, loadedSchemas]);
|
|
253
|
+
const schemasData = useMemo(() => {
|
|
254
|
+
if (!preloadAll) return [];
|
|
255
|
+
return schemas.map((src) => {
|
|
256
|
+
const raw = loadedSchemas.get(src.id) ?? null;
|
|
257
|
+
const deref = raw ? dereferenceSchema(raw) : null;
|
|
258
|
+
const resolved = resolveBaseUrl({
|
|
259
|
+
schemaSource: src.baseUrl,
|
|
260
|
+
config: configBaseUrl,
|
|
261
|
+
fromServers: raw?.servers?.[0]?.url
|
|
262
|
+
});
|
|
263
|
+
const info = raw?.info ? {
|
|
264
|
+
title: raw.info.title,
|
|
265
|
+
version: raw.info.version,
|
|
266
|
+
description: raw.info.description,
|
|
267
|
+
servers: raw.servers
|
|
268
|
+
} : null;
|
|
269
|
+
const eps = deref ? extractEndpoints(deref, resolved, src.id) : [];
|
|
270
|
+
const state = loadStates.get(src.id) ?? { loading: !raw, error: null };
|
|
271
|
+
return {
|
|
272
|
+
source: src,
|
|
273
|
+
info,
|
|
274
|
+
rawSchema: raw,
|
|
275
|
+
endpoints: eps,
|
|
276
|
+
resolvedBaseUrl: resolved || void 0,
|
|
277
|
+
loading: state.loading,
|
|
278
|
+
error: state.error
|
|
279
|
+
};
|
|
280
|
+
});
|
|
281
|
+
}, [preloadAll, schemas, loadedSchemas, loadStates, configBaseUrl]);
|
|
194
282
|
const setCurrentSchema = useCallback((schemaId) => {
|
|
195
283
|
setCurrentSchemaId(schemaId);
|
|
196
284
|
}, []);
|
|
@@ -227,10 +315,75 @@ function useOpenApiSchema({
|
|
|
227
315
|
// convention from the resolver into undefined at the API boundary.
|
|
228
316
|
resolvedBaseUrl: resolvedBaseUrl || void 0,
|
|
229
317
|
setCurrentSchema,
|
|
230
|
-
refresh
|
|
318
|
+
refresh,
|
|
319
|
+
schemasData
|
|
231
320
|
};
|
|
232
321
|
}
|
|
233
322
|
__name(useOpenApiSchema, "useOpenApiSchema");
|
|
323
|
+
function parseDocsHash(hash) {
|
|
324
|
+
const raw = hash.startsWith("#") ? hash.slice(1) : hash;
|
|
325
|
+
if (!raw) return { schemaId: null, anchor: null };
|
|
326
|
+
const [schemaId = null, ...rest] = raw.split("/");
|
|
327
|
+
const anchor = rest.length > 0 ? rest.join("/") : null;
|
|
328
|
+
return {
|
|
329
|
+
schemaId: schemaId || null,
|
|
330
|
+
anchor: anchor || null
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
__name(parseDocsHash, "parseDocsHash");
|
|
334
|
+
function buildDocsHash(schemaId, anchor) {
|
|
335
|
+
if (!schemaId && !anchor) return "";
|
|
336
|
+
if (schemaId && anchor) return `#${schemaId}/${anchor}`;
|
|
337
|
+
if (schemaId) return `#${schemaId}`;
|
|
338
|
+
return anchor ? `#${anchor}` : "";
|
|
339
|
+
}
|
|
340
|
+
__name(buildDocsHash, "buildDocsHash");
|
|
341
|
+
function useDocsUrlSync({
|
|
342
|
+
enabled,
|
|
343
|
+
currentSchemaId,
|
|
344
|
+
activeAnchor,
|
|
345
|
+
onHashTarget
|
|
346
|
+
}) {
|
|
347
|
+
const primedRef = useRef(false);
|
|
348
|
+
const onHashTargetRef = useRef(onHashTarget);
|
|
349
|
+
useEffect(() => {
|
|
350
|
+
onHashTargetRef.current = onHashTarget;
|
|
351
|
+
}, [onHashTarget]);
|
|
352
|
+
useEffect(() => {
|
|
353
|
+
if (!enabled || typeof window === "undefined") return;
|
|
354
|
+
const apply = /* @__PURE__ */ __name(() => {
|
|
355
|
+
onHashTargetRef.current(parseDocsHash(window.location.hash));
|
|
356
|
+
}, "apply");
|
|
357
|
+
apply();
|
|
358
|
+
primedRef.current = true;
|
|
359
|
+
window.addEventListener("hashchange", apply);
|
|
360
|
+
window.addEventListener("popstate", apply);
|
|
361
|
+
return () => {
|
|
362
|
+
window.removeEventListener("hashchange", apply);
|
|
363
|
+
window.removeEventListener("popstate", apply);
|
|
364
|
+
};
|
|
365
|
+
}, [enabled]);
|
|
366
|
+
useEffect(() => {
|
|
367
|
+
if (!enabled || typeof window === "undefined") return;
|
|
368
|
+
if (!primedRef.current) return;
|
|
369
|
+
const next = buildDocsHash(currentSchemaId, activeAnchor);
|
|
370
|
+
const current = window.location.hash;
|
|
371
|
+
if (next === current) return;
|
|
372
|
+
const url = next ? `${window.location.pathname}${window.location.search}${next}` : `${window.location.pathname}${window.location.search}`;
|
|
373
|
+
window.history.replaceState(window.history.state, "", url);
|
|
374
|
+
}, [enabled, currentSchemaId, activeAnchor]);
|
|
375
|
+
const pushTarget = useCallback(
|
|
376
|
+
(schemaId, anchor) => {
|
|
377
|
+
if (!enabled || typeof window === "undefined") return;
|
|
378
|
+
const next = buildDocsHash(schemaId, anchor);
|
|
379
|
+
const url = next ? `${window.location.pathname}${window.location.search}${next}` : `${window.location.pathname}${window.location.search}`;
|
|
380
|
+
window.history.pushState(window.history.state, "", url);
|
|
381
|
+
},
|
|
382
|
+
[enabled]
|
|
383
|
+
);
|
|
384
|
+
return { pushTarget };
|
|
385
|
+
}
|
|
386
|
+
__name(useDocsUrlSync, "useDocsUrlSync");
|
|
234
387
|
var EMPTY_DRAFT = { parameters: {}, requestBody: "" };
|
|
235
388
|
function storageKey(schemaId, ep) {
|
|
236
389
|
if (!schemaId || !ep) return null;
|
|
@@ -361,6 +514,18 @@ function EndpointDraftSync({ schemaId }) {
|
|
|
361
514
|
return null;
|
|
362
515
|
}
|
|
363
516
|
__name(EndpointDraftSync, "EndpointDraftSync");
|
|
517
|
+
|
|
518
|
+
// src/tools/OpenapiViewer/components/DocsLayout/anchor.ts
|
|
519
|
+
function endpointAnchor(ep, schemaId) {
|
|
520
|
+
const slug = ep.path.replace(/^https?:\/\/[^/]+/, "").replace(/[{}]/g, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase();
|
|
521
|
+
const schemaSlug = schemaId ? `${slugifySchemaId(schemaId)}-` : "";
|
|
522
|
+
return `ep-${schemaSlug}${ep.method.toLowerCase()}-${slug}`;
|
|
523
|
+
}
|
|
524
|
+
__name(endpointAnchor, "endpointAnchor");
|
|
525
|
+
function slugifySchemaId(id) {
|
|
526
|
+
return id.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
527
|
+
}
|
|
528
|
+
__name(slugifySchemaId, "slugifySchemaId");
|
|
364
529
|
var METHOD_STYLES = {
|
|
365
530
|
GET: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/25",
|
|
366
531
|
POST: "bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/25",
|
|
@@ -456,13 +621,6 @@ function CollapsibleSection({
|
|
|
456
621
|
}
|
|
457
622
|
__name(CollapsibleSection, "CollapsibleSection");
|
|
458
623
|
|
|
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
624
|
// src/tools/OpenapiViewer/components/DocsLayout/sidebarLabel.ts
|
|
467
625
|
function longestCommonPrefix(paths) {
|
|
468
626
|
if (paths.length === 0) return "";
|
|
@@ -503,6 +661,8 @@ function sidebarTooltip(ep) {
|
|
|
503
661
|
return `${ep.method} ${relativePath2(ep.path)}`;
|
|
504
662
|
}
|
|
505
663
|
__name(sidebarTooltip, "sidebarTooltip");
|
|
664
|
+
|
|
665
|
+
// src/tools/OpenapiViewer/components/DocsLayout/grouping.ts
|
|
506
666
|
var METHOD_ORDER = {
|
|
507
667
|
GET: 0,
|
|
508
668
|
POST: 1,
|
|
@@ -510,138 +670,25 @@ var METHOD_ORDER = {
|
|
|
510
670
|
PATCH: 3,
|
|
511
671
|
DELETE: 4
|
|
512
672
|
};
|
|
673
|
+
var methodRank = /* @__PURE__ */ __name((ep) => METHOD_ORDER[ep.method] ?? 99, "methodRank");
|
|
513
674
|
function groupEndpoints(list) {
|
|
514
|
-
const
|
|
515
|
-
|
|
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]) => ({
|
|
675
|
+
const byCategory = groupBy(list, "category");
|
|
676
|
+
const all = Object.entries(byCategory).map(([category, endpoints]) => ({
|
|
521
677
|
category,
|
|
522
|
-
endpoints: [
|
|
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
|
-
}),
|
|
678
|
+
endpoints: orderBy(endpoints, ["path", methodRank], ["asc", "asc"]),
|
|
527
679
|
commonPrefix: longestCommonPrefix(endpoints.map((e) => e.path))
|
|
528
680
|
}));
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
if (b.category === "Other") return -1;
|
|
532
|
-
return a.category.localeCompare(b.category);
|
|
533
|
-
});
|
|
534
|
-
return groups;
|
|
681
|
+
const [other, named] = partition(all, (g) => g.category === "Other");
|
|
682
|
+
return [...sortBy(named, (g) => g.category.toLowerCase()), ...other];
|
|
535
683
|
}
|
|
536
684
|
__name(groupEndpoints, "groupEndpoints");
|
|
537
|
-
function
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
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
|
-
] });
|
|
685
|
+
function buildSchemaSections(sources, endpointsBySchema) {
|
|
686
|
+
return sources.map((source) => ({
|
|
687
|
+
source,
|
|
688
|
+
groups: groupEndpoints(endpointsBySchema[source.id] ?? [])
|
|
689
|
+
}));
|
|
643
690
|
}
|
|
644
|
-
__name(
|
|
691
|
+
__name(buildSchemaSections, "buildSchemaSections");
|
|
645
692
|
var FLAVOUR_LABELS = {
|
|
646
693
|
markdown: {
|
|
647
694
|
title: "Markdown for LLM",
|
|
@@ -656,7 +703,7 @@ var FLAVOUR_LABELS = {
|
|
|
656
703
|
hint: "Full OpenAPI document with $refs."
|
|
657
704
|
}
|
|
658
705
|
};
|
|
659
|
-
function SchemaCopyMenu({ schema, endpoints, baseUrl }) {
|
|
706
|
+
function SchemaCopyMenu({ schema, endpoints, baseUrl, variant = "button" }) {
|
|
660
707
|
const [sizeCache, setSizeCache] = useState({});
|
|
661
708
|
const [justCopied, setJustCopied] = useState(null);
|
|
662
709
|
const [open, setOpen] = useState(false);
|
|
@@ -674,20 +721,36 @@ function SchemaCopyMenu({ schema, endpoints, baseUrl }) {
|
|
|
674
721
|
async (flavour) => {
|
|
675
722
|
if (!isReady) return;
|
|
676
723
|
const text = build(flavour);
|
|
724
|
+
const label = FLAVOUR_LABELS[flavour].title;
|
|
677
725
|
try {
|
|
678
726
|
await navigator.clipboard.writeText(text);
|
|
679
|
-
|
|
727
|
+
const size = formatBytes(text);
|
|
728
|
+
setSizeCache((prev) => ({ ...prev, [flavour]: size }));
|
|
680
729
|
setJustCopied(flavour);
|
|
681
730
|
setTimeout(() => setJustCopied(null), 1500);
|
|
682
731
|
setOpen(false);
|
|
683
|
-
|
|
732
|
+
toast.success(`Copied ${label}`, { description: size });
|
|
733
|
+
} catch (err) {
|
|
734
|
+
const message = err instanceof Error ? err.message : "Clipboard permission denied";
|
|
735
|
+
toast.error("Copy failed", { description: message });
|
|
684
736
|
}
|
|
685
737
|
},
|
|
686
738
|
[build, isReady]
|
|
687
739
|
);
|
|
688
740
|
const flavours = useMemo(() => ["markdown", "compact", "raw"], []);
|
|
689
741
|
return /* @__PURE__ */ jsxs(DropdownMenu, { open, onOpenChange: setOpen, children: [
|
|
690
|
-
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children:
|
|
742
|
+
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: variant === "icon" ? /* @__PURE__ */ jsx(
|
|
743
|
+
Button,
|
|
744
|
+
{
|
|
745
|
+
variant: "ghost",
|
|
746
|
+
size: "icon",
|
|
747
|
+
className: "h-7 w-7 shrink-0",
|
|
748
|
+
disabled: !isReady,
|
|
749
|
+
title: "Copy schema for AI",
|
|
750
|
+
"aria-label": "Copy schema for AI",
|
|
751
|
+
children: /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5" })
|
|
752
|
+
}
|
|
753
|
+
) : /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1.5 text-xs", disabled: !isReady, children: [
|
|
691
754
|
/* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }),
|
|
692
755
|
"Copy for AI",
|
|
693
756
|
/* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 opacity-60" })
|
|
@@ -725,6 +788,296 @@ function SchemaCopyMenu({ schema, endpoints, baseUrl }) {
|
|
|
725
788
|
] });
|
|
726
789
|
}
|
|
727
790
|
__name(SchemaCopyMenu, "SchemaCopyMenu");
|
|
791
|
+
var METHOD_FILTERS = ["ALL", "GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
792
|
+
function filterEndpoints(list, query, method) {
|
|
793
|
+
let out = list;
|
|
794
|
+
if (method !== "ALL") {
|
|
795
|
+
out = out.filter((e) => e.method === method);
|
|
796
|
+
}
|
|
797
|
+
if (query) {
|
|
798
|
+
const q = query.toLowerCase();
|
|
799
|
+
out = out.filter(
|
|
800
|
+
(e) => e.summary.toLowerCase().includes(q) || e.name.toLowerCase().includes(q) || e.description.toLowerCase().includes(q) || e.path.toLowerCase().includes(q)
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
return out;
|
|
804
|
+
}
|
|
805
|
+
__name(filterEndpoints, "filterEndpoints");
|
|
806
|
+
function buildCategory(group, activeEndpointId, schemaId, keyPrefix) {
|
|
807
|
+
const rows = group.endpoints.map((ep) => {
|
|
808
|
+
const anchor = endpointAnchor(ep, schemaId ?? ep.schemaId ?? null);
|
|
809
|
+
return {
|
|
810
|
+
key: `${ep.method}-${ep.path}`,
|
|
811
|
+
anchor,
|
|
812
|
+
schemaId: schemaId ?? ep.schemaId ?? null,
|
|
813
|
+
label: sidebarLabel(ep, group.commonPrefix),
|
|
814
|
+
tooltip: sidebarTooltip(ep),
|
|
815
|
+
method: ep.method,
|
|
816
|
+
useMono: !ep.summary,
|
|
817
|
+
isActive: activeEndpointId === anchor
|
|
818
|
+
};
|
|
819
|
+
});
|
|
820
|
+
return {
|
|
821
|
+
key: `${keyPrefix}${group.category}`,
|
|
822
|
+
category: group.category,
|
|
823
|
+
rows
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
__name(buildCategory, "buildCategory");
|
|
827
|
+
var emptyTextFor = /* @__PURE__ */ __name((query, method, defaultText) => {
|
|
828
|
+
if (query && method !== "ALL") return `No ${method} endpoints match "${query}"`;
|
|
829
|
+
if (query) return `No endpoints match "${query}"`;
|
|
830
|
+
if (method !== "ALL") return `No ${method} endpoints`;
|
|
831
|
+
return defaultText;
|
|
832
|
+
}, "emptyTextFor");
|
|
833
|
+
function buildFlatVM(endpoints, selectedVersion, query, method, activeEndpointId) {
|
|
834
|
+
const filtered = filterEndpoints(deduplicateEndpoints(endpoints, selectedVersion), query, method);
|
|
835
|
+
const groups = groupEndpoints(filtered);
|
|
836
|
+
return {
|
|
837
|
+
kind: "flat",
|
|
838
|
+
categories: groups.map((g) => buildCategory(g, activeEndpointId, null, "")),
|
|
839
|
+
emptyText: emptyTextFor(query, method, "No endpoints in this schema")
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
__name(buildFlatVM, "buildFlatVM");
|
|
843
|
+
function buildSectionsVM(schemas, endpointsBySchema, selectedVersion, query, method, activeEndpointId) {
|
|
844
|
+
const filteredMap = {};
|
|
845
|
+
for (const src of schemas) {
|
|
846
|
+
const raw = endpointsBySchema[src.id] ?? [];
|
|
847
|
+
filteredMap[src.id] = filterEndpoints(deduplicateEndpoints(raw, selectedVersion), query, method);
|
|
848
|
+
}
|
|
849
|
+
const rawSections = buildSchemaSections(schemas, filteredMap);
|
|
850
|
+
const sections = rawSections.filter((s) => s.groups.length > 0).map((s) => ({
|
|
851
|
+
sourceId: s.source.id,
|
|
852
|
+
sourceName: s.source.name,
|
|
853
|
+
categories: s.groups.map((g) => buildCategory(g, activeEndpointId, s.source.id, `${s.source.id}-`))
|
|
854
|
+
}));
|
|
855
|
+
return {
|
|
856
|
+
kind: "sections",
|
|
857
|
+
sections,
|
|
858
|
+
emptyText: emptyTextFor(query, method, "No endpoints in any schema")
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
__name(buildSectionsVM, "buildSectionsVM");
|
|
862
|
+
function DocsSidebar({
|
|
863
|
+
info,
|
|
864
|
+
endpoints,
|
|
865
|
+
schemas,
|
|
866
|
+
currentSchemaId,
|
|
867
|
+
onSchemaChange,
|
|
868
|
+
activeEndpointId,
|
|
869
|
+
selectedVersion,
|
|
870
|
+
onNavigate,
|
|
871
|
+
grouping = "selector",
|
|
872
|
+
endpointsBySchema,
|
|
873
|
+
rawSchema,
|
|
874
|
+
resolvedBaseUrl
|
|
875
|
+
}) {
|
|
876
|
+
const [search, setSearch] = useState("");
|
|
877
|
+
const [debounced, setDebounced] = useState("");
|
|
878
|
+
const [methodFilter, setMethodFilter] = useState("ALL");
|
|
879
|
+
useEffect(() => {
|
|
880
|
+
const id = setTimeout(() => setDebounced(search), 120);
|
|
881
|
+
return () => clearTimeout(id);
|
|
882
|
+
}, [search]);
|
|
883
|
+
const body = useMemo(() => {
|
|
884
|
+
if (grouping === "sections") {
|
|
885
|
+
return buildSectionsVM(
|
|
886
|
+
schemas,
|
|
887
|
+
endpointsBySchema ?? {},
|
|
888
|
+
selectedVersion,
|
|
889
|
+
debounced,
|
|
890
|
+
methodFilter,
|
|
891
|
+
activeEndpointId
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
return buildFlatVM(endpoints, selectedVersion, debounced, methodFilter, activeEndpointId);
|
|
895
|
+
}, [
|
|
896
|
+
grouping,
|
|
897
|
+
schemas,
|
|
898
|
+
endpointsBySchema,
|
|
899
|
+
endpoints,
|
|
900
|
+
selectedVersion,
|
|
901
|
+
debounced,
|
|
902
|
+
methodFilter,
|
|
903
|
+
activeEndpointId
|
|
904
|
+
]);
|
|
905
|
+
const schemaOptions = useMemo(
|
|
906
|
+
() => schemas.map((s) => ({ value: s.id, label: s.name })),
|
|
907
|
+
[schemas]
|
|
908
|
+
);
|
|
909
|
+
const hasMultipleSchemas = schemas.length > 1;
|
|
910
|
+
const apiTitle = info?.title ?? "API Reference";
|
|
911
|
+
const showCombobox = grouping === "selector" && hasMultipleSchemas;
|
|
912
|
+
const copyReady = rawSchema !== null && rawSchema !== void 0 && endpoints.length > 0;
|
|
913
|
+
return /* @__PURE__ */ jsxs("aside", { className: "flex flex-col h-full min-h-0 border-r bg-muted/10", children: [
|
|
914
|
+
/* @__PURE__ */ jsxs("div", { className: "shrink-0 border-b px-3 h-12 flex items-center gap-2", children: [
|
|
915
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-1 min-w-0", children: [
|
|
916
|
+
/* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold text-foreground truncate", children: apiTitle }),
|
|
917
|
+
info?.version && /* @__PURE__ */ jsxs("span", { className: "font-mono text-[10px] text-muted-foreground/70 shrink-0", children: [
|
|
918
|
+
"v",
|
|
919
|
+
info.version
|
|
920
|
+
] })
|
|
921
|
+
] }),
|
|
922
|
+
copyReady && /* @__PURE__ */ jsx(
|
|
923
|
+
SchemaCopyMenu,
|
|
924
|
+
{
|
|
925
|
+
schema: rawSchema ?? null,
|
|
926
|
+
endpoints,
|
|
927
|
+
baseUrl: resolvedBaseUrl,
|
|
928
|
+
variant: "icon"
|
|
929
|
+
}
|
|
930
|
+
)
|
|
931
|
+
] }),
|
|
932
|
+
/* @__PURE__ */ jsxs("div", { className: "shrink-0 border-b px-3 py-3 space-y-2", children: [
|
|
933
|
+
showCombobox && /* @__PURE__ */ jsx(
|
|
934
|
+
Combobox,
|
|
935
|
+
{
|
|
936
|
+
options: schemaOptions,
|
|
937
|
+
value: currentSchemaId ?? "",
|
|
938
|
+
onValueChange: (id) => id && onSchemaChange(id),
|
|
939
|
+
placeholder: "Select API",
|
|
940
|
+
searchPlaceholder: "Search APIs\u2026",
|
|
941
|
+
emptyText: "No APIs found",
|
|
942
|
+
className: "w-full h-8 text-xs"
|
|
943
|
+
}
|
|
944
|
+
),
|
|
945
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
946
|
+
/* @__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" }),
|
|
947
|
+
/* @__PURE__ */ jsx(
|
|
948
|
+
Input,
|
|
949
|
+
{
|
|
950
|
+
placeholder: "Search endpoints\u2026",
|
|
951
|
+
value: search,
|
|
952
|
+
onChange: (e) => setSearch(e.target.value),
|
|
953
|
+
className: "pl-8 h-8 text-xs"
|
|
954
|
+
}
|
|
955
|
+
)
|
|
956
|
+
] }),
|
|
957
|
+
/* @__PURE__ */ jsx(MethodChips, { value: methodFilter, onChange: setMethodFilter })
|
|
958
|
+
] }),
|
|
959
|
+
/* @__PURE__ */ jsx(ScrollArea, { children: /* @__PURE__ */ jsx(SidebarBody, { body, onNavigate }) })
|
|
960
|
+
] });
|
|
961
|
+
}
|
|
962
|
+
__name(DocsSidebar, "DocsSidebar");
|
|
963
|
+
function MethodChips({
|
|
964
|
+
value,
|
|
965
|
+
onChange
|
|
966
|
+
}) {
|
|
967
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 flex-wrap", children: METHOD_FILTERS.map((m) => {
|
|
968
|
+
const active = value === m;
|
|
969
|
+
return /* @__PURE__ */ jsx(
|
|
970
|
+
"button",
|
|
971
|
+
{
|
|
972
|
+
type: "button",
|
|
973
|
+
onClick: () => onChange(m),
|
|
974
|
+
"aria-pressed": active,
|
|
975
|
+
className: cn(
|
|
976
|
+
"px-2 py-0.5 rounded font-mono text-[10px] font-semibold tracking-wide transition-colors border",
|
|
977
|
+
active ? "bg-primary/15 border-primary/40 text-foreground" : "bg-transparent border-border/40 text-muted-foreground hover:text-foreground hover:border-border"
|
|
978
|
+
),
|
|
979
|
+
children: m
|
|
980
|
+
},
|
|
981
|
+
m
|
|
982
|
+
);
|
|
983
|
+
}) });
|
|
984
|
+
}
|
|
985
|
+
__name(MethodChips, "MethodChips");
|
|
986
|
+
function SidebarBody({ body, onNavigate }) {
|
|
987
|
+
if (body.kind === "flat") {
|
|
988
|
+
if (body.categories.length === 0) {
|
|
989
|
+
return /* @__PURE__ */ jsx("div", { className: "py-10 px-4 text-center text-xs text-muted-foreground", children: body.emptyText });
|
|
990
|
+
}
|
|
991
|
+
return /* @__PURE__ */ jsx("nav", { className: "py-2", children: body.categories.map((cat) => /* @__PURE__ */ jsx(CategoryBlock, { category: cat, onNavigate }, cat.key)) });
|
|
992
|
+
}
|
|
993
|
+
if (body.sections.length === 0) {
|
|
994
|
+
return /* @__PURE__ */ jsx("div", { className: "py-10 px-4 text-center text-xs text-muted-foreground", children: body.emptyText });
|
|
995
|
+
}
|
|
996
|
+
return /* @__PURE__ */ jsx("nav", { className: "py-2", children: body.sections.map((section) => /* @__PURE__ */ jsxs("div", { className: "mb-5 last:mb-2", children: [
|
|
997
|
+
/* @__PURE__ */ jsx("div", { className: "px-4 py-2 sticky top-0 z-[1] bg-muted/30 backdrop-blur-[2px] border-b border-border/30", children: /* @__PURE__ */ jsx("span", { className: "text-[11px] font-bold uppercase tracking-[0.12em] text-foreground/80", children: section.sourceName }) }),
|
|
998
|
+
section.categories.map((cat) => /* @__PURE__ */ jsx(CategoryBlock, { category: cat, onNavigate }, cat.key))
|
|
999
|
+
] }, section.sourceId)) });
|
|
1000
|
+
}
|
|
1001
|
+
__name(SidebarBody, "SidebarBody");
|
|
1002
|
+
var CategoryBlock = React6.memo(/* @__PURE__ */ __name(function CategoryBlock2({
|
|
1003
|
+
category,
|
|
1004
|
+
onNavigate
|
|
1005
|
+
}) {
|
|
1006
|
+
return /* @__PURE__ */ jsxs("div", { className: "mb-4 last:mb-2", children: [
|
|
1007
|
+
/* @__PURE__ */ jsx("div", { className: "px-4 py-1.5 text-[10px] font-semibold uppercase tracking-[0.14em] text-muted-foreground/50 select-none", children: category.category }),
|
|
1008
|
+
/* @__PURE__ */ jsx("div", { children: category.rows.map((row) => /* @__PURE__ */ jsx(EndpointRow, { row, onNavigate }, row.key)) })
|
|
1009
|
+
] });
|
|
1010
|
+
}, "CategoryBlock"));
|
|
1011
|
+
var EndpointRow = React6.memo(/* @__PURE__ */ __name(function EndpointRow2({
|
|
1012
|
+
row,
|
|
1013
|
+
onNavigate
|
|
1014
|
+
}) {
|
|
1015
|
+
return /* @__PURE__ */ jsxs(Tooltip, { delayDuration: 350, children: [
|
|
1016
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
1017
|
+
"button",
|
|
1018
|
+
{
|
|
1019
|
+
onClick: () => onNavigate(row.anchor, row.schemaId),
|
|
1020
|
+
"aria-current": row.isActive ? "location" : void 0,
|
|
1021
|
+
className: cn(
|
|
1022
|
+
"relative group w-full text-left flex items-start gap-2 pl-4 pr-3 py-1.5 transition-colors",
|
|
1023
|
+
row.isActive ? "bg-primary/10 text-foreground" : "hover:bg-muted/40 text-foreground/75 hover:text-foreground"
|
|
1024
|
+
),
|
|
1025
|
+
children: [
|
|
1026
|
+
row.isActive && /* @__PURE__ */ jsx("span", { className: "absolute left-0 top-1 bottom-1 w-0.5 rounded-r bg-primary" }),
|
|
1027
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 mt-[1px]", children: /* @__PURE__ */ jsx(MethodBadge, { method: row.method }) }),
|
|
1028
|
+
/* @__PURE__ */ jsx(
|
|
1029
|
+
"span",
|
|
1030
|
+
{
|
|
1031
|
+
className: cn(
|
|
1032
|
+
"line-clamp-2 leading-snug flex-1 min-w-0",
|
|
1033
|
+
row.useMono ? "font-mono text-[11px] break-all" : "text-[12px]",
|
|
1034
|
+
row.isActive && "text-foreground font-medium"
|
|
1035
|
+
),
|
|
1036
|
+
children: row.label
|
|
1037
|
+
}
|
|
1038
|
+
)
|
|
1039
|
+
]
|
|
1040
|
+
}
|
|
1041
|
+
) }),
|
|
1042
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "right", align: "center", className: "font-mono text-[11px]", children: row.tooltip })
|
|
1043
|
+
] });
|
|
1044
|
+
}, "EndpointRow"));
|
|
1045
|
+
|
|
1046
|
+
// src/tools/OpenapiViewer/utils/scrollParent.ts
|
|
1047
|
+
function getScrollParent(el) {
|
|
1048
|
+
if (typeof window === "undefined") return null;
|
|
1049
|
+
if (!el) return window;
|
|
1050
|
+
let cur = el.parentElement;
|
|
1051
|
+
while (cur && cur !== document.body && cur !== document.documentElement) {
|
|
1052
|
+
const style = getComputedStyle(cur);
|
|
1053
|
+
const overflowY = style.overflowY;
|
|
1054
|
+
const canScroll = (overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") && cur.scrollHeight > cur.clientHeight;
|
|
1055
|
+
if (canScroll) return cur;
|
|
1056
|
+
cur = cur.parentElement;
|
|
1057
|
+
}
|
|
1058
|
+
return window;
|
|
1059
|
+
}
|
|
1060
|
+
__name(getScrollParent, "getScrollParent");
|
|
1061
|
+
function getScrollTop(target) {
|
|
1062
|
+
return target === window ? window.scrollY : target.scrollTop;
|
|
1063
|
+
}
|
|
1064
|
+
__name(getScrollTop, "getScrollTop");
|
|
1065
|
+
function getViewportHeight(target) {
|
|
1066
|
+
return target === window ? window.innerHeight : target.clientHeight;
|
|
1067
|
+
}
|
|
1068
|
+
__name(getViewportHeight, "getViewportHeight");
|
|
1069
|
+
function getTargetTop(target) {
|
|
1070
|
+
return target === window ? 0 : target.getBoundingClientRect().top;
|
|
1071
|
+
}
|
|
1072
|
+
__name(getTargetTop, "getTargetTop");
|
|
1073
|
+
function scrollTargetTo(target, top) {
|
|
1074
|
+
if (target === window) {
|
|
1075
|
+
window.scrollTo({ top, behavior: "smooth" });
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
target.scrollTop = top;
|
|
1079
|
+
}
|
|
1080
|
+
__name(scrollTargetTo, "scrollTargetTo");
|
|
728
1081
|
function ApiIntroSection({ info, schema, endpoints, resolvedBaseUrl }) {
|
|
729
1082
|
return /* @__PURE__ */ jsxs("section", { className: "pb-10 mb-10 border-b", children: [
|
|
730
1083
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4 flex-wrap", children: [
|
|
@@ -828,8 +1181,9 @@ function schemaToFields(schema, prefix = "", depth = 0) {
|
|
|
828
1181
|
return rows;
|
|
829
1182
|
}
|
|
830
1183
|
__name(schemaToFields, "schemaToFields");
|
|
831
|
-
function EndpointDoc({ endpoint, isLoadedInPlayground, onTryIt }) {
|
|
832
|
-
const
|
|
1184
|
+
function EndpointDoc({ endpoint, isLoadedInPlayground, onTryIt, schemaId }) {
|
|
1185
|
+
const scopedSchemaId = schemaId ?? endpoint.schemaId ?? null;
|
|
1186
|
+
const anchor = endpointAnchor(endpoint, scopedSchemaId);
|
|
833
1187
|
const pathParams = endpoint.parameters?.filter((p) => endpoint.path.includes(`{${p.name}}`)) ?? [];
|
|
834
1188
|
const queryParams = endpoint.parameters?.filter((p) => !endpoint.path.includes(`{${p.name}}`)) ?? [];
|
|
835
1189
|
const [copied, setCopied] = useState(false);
|
|
@@ -847,6 +1201,7 @@ function EndpointDoc({ endpoint, isLoadedInPlayground, onTryIt }) {
|
|
|
847
1201
|
{
|
|
848
1202
|
id: anchor,
|
|
849
1203
|
"data-endpoint-anchor": anchor,
|
|
1204
|
+
"data-schema-id": scopedSchemaId ?? "",
|
|
850
1205
|
className: "scroll-mt-24 py-10 first:pt-0",
|
|
851
1206
|
children: [
|
|
852
1207
|
/* @__PURE__ */ jsxs("header", { className: "space-y-4", children: [
|
|
@@ -1007,52 +1362,103 @@ function StatusTag({ code }) {
|
|
|
1007
1362
|
), children: code });
|
|
1008
1363
|
}
|
|
1009
1364
|
__name(StatusTag, "StatusTag");
|
|
1010
|
-
var
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1365
|
+
var readNavbarOffset = /* @__PURE__ */ __name(() => {
|
|
1366
|
+
if (typeof document === "undefined") return 0;
|
|
1367
|
+
const raw = getComputedStyle(document.documentElement).getPropertyValue("--navbar-height");
|
|
1368
|
+
const parsed = parseInt(raw || "", 10);
|
|
1369
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
1370
|
+
}, "readNavbarOffset");
|
|
1371
|
+
var isSameEndpoint = /* @__PURE__ */ __name((a, b) => a !== null && a.method === b.method && a.path === b.path, "isSameEndpoint");
|
|
1372
|
+
function buildEndpointRow(ep, loadedEndpoint, schemaId) {
|
|
1373
|
+
const keySchema = schemaId ? `${schemaId}-` : "";
|
|
1374
|
+
return {
|
|
1375
|
+
key: `${keySchema}${ep.method}-${ep.path}`,
|
|
1376
|
+
endpoint: ep,
|
|
1377
|
+
isLoaded: isSameEndpoint(loadedEndpoint, ep),
|
|
1378
|
+
schemaId
|
|
1379
|
+
};
|
|
1380
|
+
}
|
|
1381
|
+
__name(buildEndpointRow, "buildEndpointRow");
|
|
1382
|
+
function buildSchemaSectionVM(entry, selectedVersion, loadedEndpoint) {
|
|
1383
|
+
const title = entry.info?.title ?? entry.source.name;
|
|
1384
|
+
const version = entry.info?.version ?? null;
|
|
1385
|
+
const description = entry.info?.description ?? null;
|
|
1386
|
+
let state;
|
|
1387
|
+
if (entry.loading) {
|
|
1388
|
+
state = { kind: "loading" };
|
|
1389
|
+
} else if (entry.error) {
|
|
1390
|
+
state = { kind: "error", message: entry.error };
|
|
1391
|
+
} else {
|
|
1392
|
+
const visible = deduplicateEndpoints(entry.endpoints, selectedVersion);
|
|
1393
|
+
state = visible.length === 0 ? { kind: "empty" } : {
|
|
1394
|
+
kind: "ready",
|
|
1395
|
+
rows: visible.map((ep) => buildEndpointRow(ep, loadedEndpoint, entry.source.id))
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
return {
|
|
1399
|
+
schemaId: entry.source.id,
|
|
1400
|
+
title,
|
|
1401
|
+
version,
|
|
1402
|
+
description,
|
|
1403
|
+
state,
|
|
1404
|
+
rawSchema: entry.rawSchema,
|
|
1405
|
+
baseUrl: entry.resolvedBaseUrl,
|
|
1406
|
+
allEndpoints: entry.endpoints
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
__name(buildSchemaSectionVM, "buildSchemaSectionVM");
|
|
1410
|
+
var DocsView = React6.forwardRef(/* @__PURE__ */ __name(function DocsView2(props, ref) {
|
|
1020
1411
|
const scrollRef = useRef(null);
|
|
1021
|
-
const
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
const el = root.querySelector(`[data-endpoint-anchor="${anchor}"]`);
|
|
1029
|
-
if (!el) return;
|
|
1030
|
-
el.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
1412
|
+
const scrollTargetRef = useRef(null);
|
|
1413
|
+
const { onActiveChange } = props;
|
|
1414
|
+
const ensureScrollTarget = useCallback(() => {
|
|
1415
|
+
if (scrollTargetRef.current) return scrollTargetRef.current;
|
|
1416
|
+
if (!scrollRef.current) return null;
|
|
1417
|
+
scrollTargetRef.current = getScrollParent(scrollRef.current);
|
|
1418
|
+
return scrollTargetRef.current;
|
|
1031
1419
|
}, []);
|
|
1420
|
+
const scrollToAnchor = useCallback(
|
|
1421
|
+
(anchor) => {
|
|
1422
|
+
const root = scrollRef.current;
|
|
1423
|
+
if (!root) return;
|
|
1424
|
+
const el = root.querySelector(`[data-endpoint-anchor="${anchor}"]`);
|
|
1425
|
+
if (!el) return;
|
|
1426
|
+
const target = ensureScrollTarget();
|
|
1427
|
+
if (!target) return;
|
|
1428
|
+
const navbar = readNavbarOffset();
|
|
1429
|
+
const top = el.getBoundingClientRect().top - getTargetTop(target) + getScrollTop(target) - navbar - 8;
|
|
1430
|
+
scrollTargetTo(target, top);
|
|
1431
|
+
},
|
|
1432
|
+
[ensureScrollTarget]
|
|
1433
|
+
);
|
|
1032
1434
|
React6.useImperativeHandle(ref, () => ({ scrollToAnchor }), [scrollToAnchor]);
|
|
1033
1435
|
useEffect(() => {
|
|
1034
1436
|
const root = scrollRef.current;
|
|
1035
1437
|
if (!root) return;
|
|
1438
|
+
const target = ensureScrollTarget();
|
|
1439
|
+
if (!target) return;
|
|
1036
1440
|
let rafId = 0;
|
|
1037
1441
|
let lastActive = null;
|
|
1038
1442
|
const compute = /* @__PURE__ */ __name(() => {
|
|
1039
1443
|
rafId = 0;
|
|
1040
1444
|
const sections = root.querySelectorAll("[data-endpoint-anchor]");
|
|
1041
1445
|
if (sections.length === 0) return;
|
|
1042
|
-
const
|
|
1043
|
-
const
|
|
1446
|
+
const navbar = readNavbarOffset();
|
|
1447
|
+
const viewportTop = getTargetTop(target);
|
|
1448
|
+
const threshold = viewportTop + navbar + getViewportHeight(target) * 0.25;
|
|
1044
1449
|
let active = null;
|
|
1045
1450
|
for (const s of Array.from(sections)) {
|
|
1046
1451
|
const top = s.getBoundingClientRect().top;
|
|
1047
1452
|
if (top <= threshold) {
|
|
1048
|
-
active = s
|
|
1453
|
+
active = s;
|
|
1049
1454
|
} else {
|
|
1050
1455
|
break;
|
|
1051
1456
|
}
|
|
1052
1457
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1458
|
+
const anchor = active?.dataset.endpointAnchor ?? null;
|
|
1459
|
+
if (anchor !== lastActive) {
|
|
1460
|
+
lastActive = anchor;
|
|
1461
|
+
onActiveChange(anchor, active?.dataset.schemaId || null);
|
|
1056
1462
|
}
|
|
1057
1463
|
}, "compute");
|
|
1058
1464
|
const onScroll = /* @__PURE__ */ __name(() => {
|
|
@@ -1060,13 +1466,39 @@ var DocsView = React6.forwardRef(/* @__PURE__ */ __name(function DocsView2({
|
|
|
1060
1466
|
rafId = requestAnimationFrame(compute);
|
|
1061
1467
|
}, "onScroll");
|
|
1062
1468
|
compute();
|
|
1063
|
-
|
|
1469
|
+
target.addEventListener("scroll", onScroll, { passive: true });
|
|
1470
|
+
window.addEventListener("resize", onScroll, { passive: true });
|
|
1064
1471
|
return () => {
|
|
1065
|
-
|
|
1472
|
+
target.removeEventListener("scroll", onScroll);
|
|
1473
|
+
window.removeEventListener("resize", onScroll);
|
|
1066
1474
|
if (rafId) cancelAnimationFrame(rafId);
|
|
1067
1475
|
};
|
|
1068
|
-
}, [
|
|
1069
|
-
|
|
1476
|
+
}, [onActiveChange, ensureScrollTarget, props]);
|
|
1477
|
+
if (props.grouping === "sections") {
|
|
1478
|
+
return /* @__PURE__ */ jsx(SectionsBody, { scrollRef, ...props });
|
|
1479
|
+
}
|
|
1480
|
+
return /* @__PURE__ */ jsx(SelectorBody, { scrollRef, ...props });
|
|
1481
|
+
}, "DocsView"));
|
|
1482
|
+
function SelectorBody({
|
|
1483
|
+
scrollRef,
|
|
1484
|
+
info,
|
|
1485
|
+
rawSchema,
|
|
1486
|
+
resolvedBaseUrl,
|
|
1487
|
+
endpoints,
|
|
1488
|
+
selectedVersion,
|
|
1489
|
+
loadedEndpoint,
|
|
1490
|
+
onTryEndpoint
|
|
1491
|
+
}) {
|
|
1492
|
+
const visibleEndpoints = useMemo(
|
|
1493
|
+
() => deduplicateEndpoints(endpoints, selectedVersion),
|
|
1494
|
+
[endpoints, selectedVersion]
|
|
1495
|
+
);
|
|
1496
|
+
const rows = useMemo(
|
|
1497
|
+
() => visibleEndpoints.map((ep) => buildEndpointRow(ep, loadedEndpoint, ep.schemaId ?? null)),
|
|
1498
|
+
[visibleEndpoints, loadedEndpoint]
|
|
1499
|
+
);
|
|
1500
|
+
const isEmpty = rows.length === 0;
|
|
1501
|
+
return /* @__PURE__ */ jsx("div", { ref: scrollRef, children: /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-[860px] px-6 md:px-10 lg:px-14 py-12", children: [
|
|
1070
1502
|
info && /* @__PURE__ */ jsx(
|
|
1071
1503
|
ApiIntroSection,
|
|
1072
1504
|
{
|
|
@@ -1076,20 +1508,96 @@ var DocsView = React6.forwardRef(/* @__PURE__ */ __name(function DocsView2({
|
|
|
1076
1508
|
resolvedBaseUrl
|
|
1077
1509
|
}
|
|
1078
1510
|
),
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1511
|
+
isEmpty ? /* @__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: rows.map((row) => /* @__PURE__ */ jsx(
|
|
1512
|
+
EndpointDoc,
|
|
1513
|
+
{
|
|
1514
|
+
endpoint: row.endpoint,
|
|
1515
|
+
isLoadedInPlayground: row.isLoaded,
|
|
1516
|
+
onTryIt: () => onTryEndpoint(row.endpoint),
|
|
1517
|
+
schemaId: row.schemaId
|
|
1518
|
+
},
|
|
1519
|
+
row.key
|
|
1520
|
+
)) })
|
|
1521
|
+
] }) });
|
|
1522
|
+
}
|
|
1523
|
+
__name(SelectorBody, "SelectorBody");
|
|
1524
|
+
function SectionsBody({
|
|
1525
|
+
scrollRef,
|
|
1526
|
+
schemasData,
|
|
1527
|
+
selectedVersion,
|
|
1528
|
+
loadedEndpoint,
|
|
1529
|
+
onTryEndpoint
|
|
1530
|
+
}) {
|
|
1531
|
+
const sections = useMemo(
|
|
1532
|
+
() => schemasData.map((e) => buildSchemaSectionVM(e, selectedVersion, loadedEndpoint)),
|
|
1533
|
+
[schemasData, selectedVersion, loadedEndpoint]
|
|
1534
|
+
);
|
|
1535
|
+
return /* @__PURE__ */ jsx("div", { ref: scrollRef, children: /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-[860px] px-6 md:px-10 lg:px-14 py-12 space-y-16", children: sections.map((section) => /* @__PURE__ */ jsx(SchemaSectionView, { section, onTryEndpoint }, section.schemaId)) }) });
|
|
1536
|
+
}
|
|
1537
|
+
__name(SectionsBody, "SectionsBody");
|
|
1538
|
+
var SchemaSectionView = React6.memo(/* @__PURE__ */ __name(function SchemaSectionView2({
|
|
1539
|
+
section,
|
|
1540
|
+
onTryEndpoint
|
|
1541
|
+
}) {
|
|
1542
|
+
const canCopy = section.rawSchema !== null && section.allEndpoints.length > 0;
|
|
1543
|
+
return /* @__PURE__ */ jsxs("section", { "data-schema-anchor": section.schemaId, className: "scroll-mt-20", children: [
|
|
1544
|
+
/* @__PURE__ */ jsxs("header", { className: "mb-8 pb-4 border-b", children: [
|
|
1545
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
|
|
1546
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-3 min-w-0", children: [
|
|
1547
|
+
/* @__PURE__ */ jsx("h2", { className: "text-2xl font-semibold tracking-tight", children: section.title }),
|
|
1548
|
+
section.version && /* @__PURE__ */ jsxs("span", { className: "font-mono text-xs text-muted-foreground/70", children: [
|
|
1549
|
+
"v",
|
|
1550
|
+
section.version
|
|
1551
|
+
] })
|
|
1552
|
+
] }),
|
|
1553
|
+
canCopy && /* @__PURE__ */ jsx(
|
|
1554
|
+
SchemaCopyMenu,
|
|
1555
|
+
{
|
|
1556
|
+
schema: section.rawSchema,
|
|
1557
|
+
endpoints: section.allEndpoints,
|
|
1558
|
+
baseUrl: section.baseUrl
|
|
1559
|
+
}
|
|
1560
|
+
)
|
|
1561
|
+
] }),
|
|
1562
|
+
section.description && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground whitespace-pre-wrap", children: section.description })
|
|
1563
|
+
] }),
|
|
1564
|
+
/* @__PURE__ */ jsx(SchemaSectionStateView, { section, onTryEndpoint })
|
|
1565
|
+
] });
|
|
1566
|
+
}, "SchemaSectionView"));
|
|
1567
|
+
function SchemaSectionStateView({
|
|
1568
|
+
section,
|
|
1569
|
+
onTryEndpoint
|
|
1570
|
+
}) {
|
|
1571
|
+
switch (section.state.kind) {
|
|
1572
|
+
case "loading":
|
|
1573
|
+
return /* @__PURE__ */ jsxs("div", { className: "py-8 text-center text-sm text-muted-foreground", children: [
|
|
1574
|
+
"Loading ",
|
|
1575
|
+
section.title,
|
|
1576
|
+
"\u2026"
|
|
1577
|
+
] });
|
|
1578
|
+
case "error":
|
|
1579
|
+
return /* @__PURE__ */ jsxs("div", { className: "py-8 text-center text-sm text-destructive", children: [
|
|
1580
|
+
"Failed to load ",
|
|
1581
|
+
section.title,
|
|
1582
|
+
": ",
|
|
1583
|
+
section.state.message
|
|
1584
|
+
] });
|
|
1585
|
+
case "empty":
|
|
1586
|
+
return /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-sm text-muted-foreground", children: "No endpoints in this schema." });
|
|
1587
|
+
case "ready":
|
|
1588
|
+
return /* @__PURE__ */ jsx("div", { className: "divide-y divide-border/60", children: section.state.rows.map((row) => /* @__PURE__ */ jsx(
|
|
1082
1589
|
EndpointDoc,
|
|
1083
1590
|
{
|
|
1084
|
-
endpoint:
|
|
1085
|
-
isLoadedInPlayground: isLoaded,
|
|
1086
|
-
onTryIt: () => onTryEndpoint(
|
|
1591
|
+
endpoint: row.endpoint,
|
|
1592
|
+
isLoadedInPlayground: row.isLoaded,
|
|
1593
|
+
onTryIt: () => onTryEndpoint(row.endpoint),
|
|
1594
|
+
schemaId: row.schemaId
|
|
1087
1595
|
},
|
|
1088
|
-
|
|
1089
|
-
);
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1596
|
+
row.key
|
|
1597
|
+
)) });
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
__name(SchemaSectionStateView, "SchemaSectionStateView");
|
|
1093
1601
|
var MAX_DEPTH2 = 6;
|
|
1094
1602
|
function defaultForSchema(schema) {
|
|
1095
1603
|
if (!schema) return null;
|
|
@@ -1882,6 +2390,9 @@ var DocsLayout = /* @__PURE__ */ __name(() => {
|
|
|
1882
2390
|
const { state, config, setSelectedEndpoint } = usePlaygroundContext();
|
|
1883
2391
|
const isDesktop = useMediaQuery("(min-width: 1024px)");
|
|
1884
2392
|
const isMobile = !isDesktop;
|
|
2393
|
+
const grouping = config.schemaGrouping ?? "selector";
|
|
2394
|
+
const preloadAll = grouping === "sections";
|
|
2395
|
+
const urlSyncEnabled = typeof config.urlSync === "boolean" ? config.urlSync : Boolean(config.urlSync?.enabled);
|
|
1885
2396
|
const {
|
|
1886
2397
|
endpoints,
|
|
1887
2398
|
schemaInfo,
|
|
@@ -1891,16 +2402,26 @@ var DocsLayout = /* @__PURE__ */ __name(() => {
|
|
|
1891
2402
|
error,
|
|
1892
2403
|
schemas,
|
|
1893
2404
|
currentSchema,
|
|
1894
|
-
setCurrentSchema
|
|
2405
|
+
setCurrentSchema,
|
|
2406
|
+
schemasData
|
|
1895
2407
|
} = useOpenApiSchema({
|
|
1896
2408
|
schemas: config.schemas,
|
|
1897
2409
|
defaultSchemaId: config.defaultSchemaId,
|
|
1898
|
-
baseUrl: config.baseUrl
|
|
2410
|
+
baseUrl: config.baseUrl,
|
|
2411
|
+
preloadAll
|
|
1899
2412
|
});
|
|
1900
2413
|
const [activeAnchor, setActiveAnchor] = useState(null);
|
|
2414
|
+
const [activeSchemaId, setActiveSchemaId] = useState(null);
|
|
1901
2415
|
const [sheetOpen, setSheetOpen] = useState(false);
|
|
1902
2416
|
const docsRef = useRef(null);
|
|
1903
2417
|
const slideOpen = !isMobile && state.selectedEndpoint !== null;
|
|
2418
|
+
const endpointsBySchema = useMemo(() => {
|
|
2419
|
+
if (grouping !== "sections") return {};
|
|
2420
|
+
const byId = keyBy(schemasData, (e) => e.source.id);
|
|
2421
|
+
const out = {};
|
|
2422
|
+
for (const src of schemas) out[src.id] = byId[src.id]?.endpoints ?? [];
|
|
2423
|
+
return out;
|
|
2424
|
+
}, [grouping, schemasData, schemas]);
|
|
1904
2425
|
const handleTry = useCallback(
|
|
1905
2426
|
(ep) => {
|
|
1906
2427
|
setSelectedEndpoint(ep);
|
|
@@ -1911,29 +2432,71 @@ var DocsLayout = /* @__PURE__ */ __name(() => {
|
|
|
1911
2432
|
const handleCloseSlide = useCallback(() => {
|
|
1912
2433
|
setSelectedEndpoint(null);
|
|
1913
2434
|
}, [setSelectedEndpoint]);
|
|
1914
|
-
const handleNavigate = useCallback(
|
|
1915
|
-
|
|
2435
|
+
const handleNavigate = useCallback(
|
|
2436
|
+
(anchor, schemaId) => {
|
|
2437
|
+
if (schemaId && schemaId !== currentSchema?.id && grouping === "selector") {
|
|
2438
|
+
setCurrentSchema(schemaId);
|
|
2439
|
+
requestAnimationFrame(() => {
|
|
2440
|
+
docsRef.current?.scrollToAnchor(anchor);
|
|
2441
|
+
});
|
|
2442
|
+
return;
|
|
2443
|
+
}
|
|
2444
|
+
docsRef.current?.scrollToAnchor(anchor);
|
|
2445
|
+
},
|
|
2446
|
+
[currentSchema?.id, grouping, setCurrentSchema]
|
|
2447
|
+
);
|
|
2448
|
+
const handleActiveChange = useCallback((anchor, schemaId) => {
|
|
2449
|
+
setActiveAnchor(anchor);
|
|
2450
|
+
setActiveSchemaId(schemaId);
|
|
1916
2451
|
}, []);
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
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
|
-
]
|
|
2452
|
+
const effectiveSchemaId = grouping === "sections" ? activeSchemaId : currentSchema?.id ?? null;
|
|
2453
|
+
const handleHashTarget = useCallback(
|
|
2454
|
+
(target) => {
|
|
2455
|
+
if (!target.schemaId && !target.anchor) return;
|
|
2456
|
+
const matched = target.schemaId ? schemas.find((s) => s.id === target.schemaId || slugifySchemaId(s.id) === target.schemaId) : null;
|
|
2457
|
+
const needsSchemaSwitch = matched && grouping === "selector" && matched.id !== currentSchema?.id;
|
|
2458
|
+
if (needsSchemaSwitch) {
|
|
2459
|
+
setCurrentSchema(matched.id);
|
|
1935
2460
|
}
|
|
1936
|
-
|
|
2461
|
+
if (target.anchor) {
|
|
2462
|
+
const anchor = target.anchor;
|
|
2463
|
+
if (needsSchemaSwitch) {
|
|
2464
|
+
requestAnimationFrame(() => {
|
|
2465
|
+
docsRef.current?.scrollToAnchor(anchor);
|
|
2466
|
+
});
|
|
2467
|
+
} else {
|
|
2468
|
+
docsRef.current?.scrollToAnchor(anchor);
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
},
|
|
2472
|
+
[schemas, grouping, currentSchema?.id, setCurrentSchema]
|
|
2473
|
+
);
|
|
2474
|
+
useDocsUrlSync({
|
|
2475
|
+
enabled: urlSyncEnabled,
|
|
2476
|
+
currentSchemaId: effectiveSchemaId,
|
|
2477
|
+
activeAnchor,
|
|
2478
|
+
onHashTarget: handleHashTarget
|
|
2479
|
+
});
|
|
2480
|
+
if (loading) {
|
|
2481
|
+
return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[260px_1fr] items-start", children: [
|
|
2482
|
+
/* @__PURE__ */ jsx(
|
|
2483
|
+
"div",
|
|
2484
|
+
{
|
|
2485
|
+
className: "sticky top-[var(--navbar-height,64px)] border-r p-3 space-y-1.5 overflow-y-auto",
|
|
2486
|
+
style: { height: "calc(100dvh - var(--navbar-height, 64px))" },
|
|
2487
|
+
children: Array.from({ length: 12 }).map((_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-full rounded" }, i))
|
|
2488
|
+
}
|
|
2489
|
+
),
|
|
2490
|
+
/* @__PURE__ */ jsxs("div", { className: "p-8 space-y-4", children: [
|
|
2491
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-1/2" }),
|
|
2492
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" }),
|
|
2493
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-3/4" }),
|
|
2494
|
+
/* @__PURE__ */ jsx("div", { className: "mt-8 space-y-6", children: Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2495
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-1/3" }),
|
|
2496
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-20 w-full" })
|
|
2497
|
+
] }, i)) })
|
|
2498
|
+
] })
|
|
2499
|
+
] });
|
|
1937
2500
|
}
|
|
1938
2501
|
if (error) {
|
|
1939
2502
|
return /* @__PURE__ */ jsx(
|
|
@@ -1949,40 +2512,44 @@ var DocsLayout = /* @__PURE__ */ __name(() => {
|
|
|
1949
2512
|
);
|
|
1950
2513
|
}
|
|
1951
2514
|
if (isMobile) {
|
|
1952
|
-
return /* @__PURE__ */ jsxs(
|
|
2515
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
2516
|
+
/* @__PURE__ */ jsx(EndpointDraftSync, { schemaId: currentSchema?.id ?? null }),
|
|
2517
|
+
grouping === "sections" ? /* @__PURE__ */ jsx(
|
|
2518
|
+
DocsView,
|
|
2519
|
+
{
|
|
2520
|
+
ref: docsRef,
|
|
2521
|
+
grouping: "sections",
|
|
2522
|
+
schemasData,
|
|
2523
|
+
selectedVersion: state.selectedVersion,
|
|
2524
|
+
loadedEndpoint: state.selectedEndpoint,
|
|
2525
|
+
onTryEndpoint: handleTry,
|
|
2526
|
+
onActiveChange: handleActiveChange
|
|
2527
|
+
}
|
|
2528
|
+
) : /* @__PURE__ */ jsx(
|
|
2529
|
+
DocsView,
|
|
2530
|
+
{
|
|
2531
|
+
ref: docsRef,
|
|
2532
|
+
info: schemaInfo,
|
|
2533
|
+
rawSchema,
|
|
2534
|
+
resolvedBaseUrl,
|
|
2535
|
+
endpoints,
|
|
2536
|
+
selectedVersion: state.selectedVersion,
|
|
2537
|
+
loadedEndpoint: state.selectedEndpoint,
|
|
2538
|
+
onTryEndpoint: handleTry,
|
|
2539
|
+
onActiveChange: handleActiveChange
|
|
2540
|
+
}
|
|
2541
|
+
),
|
|
2542
|
+
/* @__PURE__ */ jsx(TryItSheet, { open: sheetOpen, onOpenChange: setSheetOpen })
|
|
2543
|
+
] });
|
|
2544
|
+
}
|
|
2545
|
+
return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 350, children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[260px_minmax(0,1fr)] items-start", children: [
|
|
2546
|
+
/* @__PURE__ */ jsx(EndpointDraftSync, { schemaId: currentSchema?.id ?? null }),
|
|
2547
|
+
/* @__PURE__ */ jsx(
|
|
1953
2548
|
"div",
|
|
1954
2549
|
{
|
|
1955
|
-
className: "
|
|
2550
|
+
className: "sticky top-[var(--navbar-height,64px)]",
|
|
1956
2551
|
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(
|
|
2552
|
+
children: /* @__PURE__ */ jsx(
|
|
1986
2553
|
DocsSidebar,
|
|
1987
2554
|
{
|
|
1988
2555
|
info: schemaInfo,
|
|
@@ -1992,29 +2559,44 @@ var DocsLayout = /* @__PURE__ */ __name(() => {
|
|
|
1992
2559
|
onSchemaChange: setCurrentSchema,
|
|
1993
2560
|
activeEndpointId: activeAnchor,
|
|
1994
2561
|
selectedVersion: state.selectedVersion,
|
|
1995
|
-
onNavigate: handleNavigate
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
/* @__PURE__ */ jsx(
|
|
1999
|
-
DocsView,
|
|
2000
|
-
{
|
|
2001
|
-
ref: docsRef,
|
|
2002
|
-
info: schemaInfo,
|
|
2562
|
+
onNavigate: handleNavigate,
|
|
2563
|
+
grouping,
|
|
2564
|
+
endpointsBySchema,
|
|
2003
2565
|
rawSchema,
|
|
2004
|
-
resolvedBaseUrl
|
|
2005
|
-
endpoints,
|
|
2006
|
-
selectedVersion: state.selectedVersion,
|
|
2007
|
-
loadedEndpoint: state.selectedEndpoint,
|
|
2008
|
-
onTryEndpoint: handleTry,
|
|
2009
|
-
onActiveChange: setActiveAnchor
|
|
2566
|
+
resolvedBaseUrl
|
|
2010
2567
|
}
|
|
2011
|
-
)
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2568
|
+
)
|
|
2569
|
+
}
|
|
2570
|
+
),
|
|
2571
|
+
grouping === "sections" ? /* @__PURE__ */ jsx(
|
|
2572
|
+
DocsView,
|
|
2573
|
+
{
|
|
2574
|
+
ref: docsRef,
|
|
2575
|
+
grouping: "sections",
|
|
2576
|
+
schemasData,
|
|
2577
|
+
selectedVersion: state.selectedVersion,
|
|
2578
|
+
loadedEndpoint: state.selectedEndpoint,
|
|
2579
|
+
onTryEndpoint: handleTry,
|
|
2580
|
+
onActiveChange: handleActiveChange
|
|
2581
|
+
}
|
|
2582
|
+
) : /* @__PURE__ */ jsx(
|
|
2583
|
+
DocsView,
|
|
2584
|
+
{
|
|
2585
|
+
ref: docsRef,
|
|
2586
|
+
info: schemaInfo,
|
|
2587
|
+
rawSchema,
|
|
2588
|
+
resolvedBaseUrl,
|
|
2589
|
+
endpoints,
|
|
2590
|
+
selectedVersion: state.selectedVersion,
|
|
2591
|
+
loadedEndpoint: state.selectedEndpoint,
|
|
2592
|
+
onTryEndpoint: handleTry,
|
|
2593
|
+
onActiveChange: handleActiveChange
|
|
2594
|
+
}
|
|
2595
|
+
),
|
|
2596
|
+
/* @__PURE__ */ jsx(SlideInPlayground, { open: slideOpen, onClose: handleCloseSlide })
|
|
2597
|
+
] }) });
|
|
2016
2598
|
}, "DocsLayout");
|
|
2017
2599
|
|
|
2018
2600
|
export { DocsLayout };
|
|
2019
|
-
//# sourceMappingURL=DocsLayout-
|
|
2020
|
-
//# sourceMappingURL=DocsLayout-
|
|
2601
|
+
//# sourceMappingURL=DocsLayout-TKJQ5W5E.mjs.map
|
|
2602
|
+
//# sourceMappingURL=DocsLayout-TKJQ5W5E.mjs.map
|