@brainfish-ai/devdoc 0.1.21
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/LICENSE +33 -0
- package/README.md +415 -0
- package/bin/devdoc.js +13 -0
- package/dist/cli/commands/build.d.ts +5 -0
- package/dist/cli/commands/build.js +87 -0
- package/dist/cli/commands/check.d.ts +1 -0
- package/dist/cli/commands/check.js +143 -0
- package/dist/cli/commands/create.d.ts +24 -0
- package/dist/cli/commands/create.js +387 -0
- package/dist/cli/commands/deploy.d.ts +9 -0
- package/dist/cli/commands/deploy.js +433 -0
- package/dist/cli/commands/dev.d.ts +6 -0
- package/dist/cli/commands/dev.js +139 -0
- package/dist/cli/commands/init.d.ts +11 -0
- package/dist/cli/commands/init.js +238 -0
- package/dist/cli/commands/keys.d.ts +12 -0
- package/dist/cli/commands/keys.js +165 -0
- package/dist/cli/commands/start.d.ts +5 -0
- package/dist/cli/commands/start.js +56 -0
- package/dist/cli/commands/upload.d.ts +13 -0
- package/dist/cli/commands/upload.js +238 -0
- package/dist/cli/commands/whoami.d.ts +8 -0
- package/dist/cli/commands/whoami.js +91 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +106 -0
- package/dist/config/index.d.ts +80 -0
- package/dist/config/index.js +133 -0
- package/dist/constants.d.ts +9 -0
- package/dist/constants.js +13 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +12 -0
- package/dist/utils/logger.d.ts +16 -0
- package/dist/utils/logger.js +61 -0
- package/dist/utils/paths.d.ts +16 -0
- package/dist/utils/paths.js +50 -0
- package/package.json +51 -0
- package/renderer/app/api/assets/[...path]/route.ts +123 -0
- package/renderer/app/api/assets/route.ts +124 -0
- package/renderer/app/api/assets/upload/route.ts +177 -0
- package/renderer/app/api/auth-schemes/route.ts +77 -0
- package/renderer/app/api/chat/route.ts +858 -0
- package/renderer/app/api/codegen/route.ts +72 -0
- package/renderer/app/api/collections/route.ts +1016 -0
- package/renderer/app/api/debug/route.ts +53 -0
- package/renderer/app/api/deploy/route.ts +234 -0
- package/renderer/app/api/device/route.ts +42 -0
- package/renderer/app/api/docs/route.ts +187 -0
- package/renderer/app/api/keys/regenerate/route.ts +80 -0
- package/renderer/app/api/openapi-spec/route.ts +151 -0
- package/renderer/app/api/projects/[slug]/route.ts +153 -0
- package/renderer/app/api/projects/[slug]/stats/route.ts +96 -0
- package/renderer/app/api/projects/register/route.ts +152 -0
- package/renderer/app/api/proxy/route.ts +149 -0
- package/renderer/app/api/proxy-stream/route.ts +168 -0
- package/renderer/app/api/redirects/route.ts +47 -0
- package/renderer/app/api/schema/route.ts +65 -0
- package/renderer/app/api/subdomains/check/route.ts +172 -0
- package/renderer/app/api/suggestions/route.ts +144 -0
- package/renderer/app/favicon.ico +0 -0
- package/renderer/app/globals.css +1103 -0
- package/renderer/app/layout.tsx +47 -0
- package/renderer/app/llms-full.txt/route.ts +346 -0
- package/renderer/app/llms.txt/route.ts +279 -0
- package/renderer/app/page.tsx +14 -0
- package/renderer/app/robots.txt/route.ts +84 -0
- package/renderer/app/sitemap.xml/route.ts +199 -0
- package/renderer/components/docs/index.ts +12 -0
- package/renderer/components/docs/mdx/accordion.tsx +169 -0
- package/renderer/components/docs/mdx/badge.tsx +132 -0
- package/renderer/components/docs/mdx/callouts.tsx +154 -0
- package/renderer/components/docs/mdx/cards.tsx +213 -0
- package/renderer/components/docs/mdx/changelog.tsx +120 -0
- package/renderer/components/docs/mdx/code-block.tsx +186 -0
- package/renderer/components/docs/mdx/code-group.tsx +421 -0
- package/renderer/components/docs/mdx/file-embeds.tsx +105 -0
- package/renderer/components/docs/mdx/frame.tsx +112 -0
- package/renderer/components/docs/mdx/highlight.tsx +151 -0
- package/renderer/components/docs/mdx/iframe.tsx +134 -0
- package/renderer/components/docs/mdx/image.tsx +235 -0
- package/renderer/components/docs/mdx/index.ts +204 -0
- package/renderer/components/docs/mdx/mermaid.tsx +240 -0
- package/renderer/components/docs/mdx/param-field.tsx +200 -0
- package/renderer/components/docs/mdx/steps.tsx +113 -0
- package/renderer/components/docs/mdx/tabs.tsx +86 -0
- package/renderer/components/docs/mdx-renderer.tsx +100 -0
- package/renderer/components/docs/navigation/breadcrumbs.tsx +76 -0
- package/renderer/components/docs/navigation/index.ts +8 -0
- package/renderer/components/docs/navigation/page-nav.tsx +64 -0
- package/renderer/components/docs/navigation/sidebar.tsx +515 -0
- package/renderer/components/docs/navigation/toc.tsx +113 -0
- package/renderer/components/docs/notice.tsx +105 -0
- package/renderer/components/docs-header.tsx +274 -0
- package/renderer/components/docs-viewer/agent/agent-chat.tsx +2076 -0
- package/renderer/components/docs-viewer/agent/cards/debug-context-card.tsx +90 -0
- package/renderer/components/docs-viewer/agent/cards/endpoint-context-card.tsx +49 -0
- package/renderer/components/docs-viewer/agent/cards/index.tsx +50 -0
- package/renderer/components/docs-viewer/agent/cards/response-options-card.tsx +212 -0
- package/renderer/components/docs-viewer/agent/cards/types.ts +84 -0
- package/renderer/components/docs-viewer/agent/chat-message.tsx +17 -0
- package/renderer/components/docs-viewer/agent/index.tsx +6 -0
- package/renderer/components/docs-viewer/agent/messages/assistant-message.tsx +119 -0
- package/renderer/components/docs-viewer/agent/messages/chat-message.tsx +46 -0
- package/renderer/components/docs-viewer/agent/messages/index.ts +17 -0
- package/renderer/components/docs-viewer/agent/messages/tool-call-display.tsx +721 -0
- package/renderer/components/docs-viewer/agent/messages/types.ts +61 -0
- package/renderer/components/docs-viewer/agent/messages/typing-indicator.tsx +24 -0
- package/renderer/components/docs-viewer/agent/messages/user-message.tsx +51 -0
- package/renderer/components/docs-viewer/code-editor/index.tsx +2 -0
- package/renderer/components/docs-viewer/code-editor/notes-mode.tsx +1283 -0
- package/renderer/components/docs-viewer/content/changelog-page.tsx +331 -0
- package/renderer/components/docs-viewer/content/doc-page.tsx +285 -0
- package/renderer/components/docs-viewer/content/documentation-viewer.tsx +17 -0
- package/renderer/components/docs-viewer/content/index.tsx +29 -0
- package/renderer/components/docs-viewer/content/introduction.tsx +21 -0
- package/renderer/components/docs-viewer/content/request-details.tsx +330 -0
- package/renderer/components/docs-viewer/content/sections/auth.tsx +69 -0
- package/renderer/components/docs-viewer/content/sections/body.tsx +66 -0
- package/renderer/components/docs-viewer/content/sections/headers.tsx +43 -0
- package/renderer/components/docs-viewer/content/sections/overview.tsx +40 -0
- package/renderer/components/docs-viewer/content/sections/parameters.tsx +43 -0
- package/renderer/components/docs-viewer/content/sections/responses.tsx +87 -0
- package/renderer/components/docs-viewer/global-auth-modal.tsx +352 -0
- package/renderer/components/docs-viewer/index.tsx +1466 -0
- package/renderer/components/docs-viewer/playground/auth-editor.tsx +280 -0
- package/renderer/components/docs-viewer/playground/body-editor.tsx +221 -0
- package/renderer/components/docs-viewer/playground/code-editor.tsx +224 -0
- package/renderer/components/docs-viewer/playground/code-snippet.tsx +387 -0
- package/renderer/components/docs-viewer/playground/graphql-playground.tsx +745 -0
- package/renderer/components/docs-viewer/playground/index.tsx +671 -0
- package/renderer/components/docs-viewer/playground/key-value-editor.tsx +261 -0
- package/renderer/components/docs-viewer/playground/method-selector.tsx +60 -0
- package/renderer/components/docs-viewer/playground/request-builder.tsx +179 -0
- package/renderer/components/docs-viewer/playground/request-tabs.tsx +237 -0
- package/renderer/components/docs-viewer/playground/response-cards/idle-card.tsx +21 -0
- package/renderer/components/docs-viewer/playground/response-cards/index.tsx +93 -0
- package/renderer/components/docs-viewer/playground/response-cards/loading-card.tsx +16 -0
- package/renderer/components/docs-viewer/playground/response-cards/network-error-card.tsx +23 -0
- package/renderer/components/docs-viewer/playground/response-cards/response-body-card.tsx +268 -0
- package/renderer/components/docs-viewer/playground/response-cards/types.ts +82 -0
- package/renderer/components/docs-viewer/playground/response-viewer.tsx +43 -0
- package/renderer/components/docs-viewer/search/index.ts +2 -0
- package/renderer/components/docs-viewer/search/search-dialog.tsx +331 -0
- package/renderer/components/docs-viewer/search/use-search.ts +117 -0
- package/renderer/components/docs-viewer/shared/markdown-renderer.tsx +431 -0
- package/renderer/components/docs-viewer/shared/method-badge.tsx +41 -0
- package/renderer/components/docs-viewer/shared/schema-viewer.tsx +349 -0
- package/renderer/components/docs-viewer/sidebar/collection-tree.tsx +239 -0
- package/renderer/components/docs-viewer/sidebar/endpoint-options.tsx +316 -0
- package/renderer/components/docs-viewer/sidebar/index.tsx +343 -0
- package/renderer/components/docs-viewer/sidebar/right-sidebar.tsx +202 -0
- package/renderer/components/docs-viewer/sidebar/sidebar-group.tsx +118 -0
- package/renderer/components/docs-viewer/sidebar/sidebar-item.tsx +226 -0
- package/renderer/components/docs-viewer/sidebar/sidebar-section.tsx +52 -0
- package/renderer/components/theme-provider.tsx +11 -0
- package/renderer/components/theme-toggle.tsx +76 -0
- package/renderer/components/ui/badge.tsx +46 -0
- package/renderer/components/ui/button.tsx +59 -0
- package/renderer/components/ui/dialog.tsx +118 -0
- package/renderer/components/ui/dropdown-menu.tsx +257 -0
- package/renderer/components/ui/input.tsx +21 -0
- package/renderer/components/ui/label.tsx +24 -0
- package/renderer/components/ui/navigation-menu.tsx +168 -0
- package/renderer/components/ui/select.tsx +190 -0
- package/renderer/components/ui/spinner.tsx +114 -0
- package/renderer/components/ui/tabs.tsx +66 -0
- package/renderer/components/ui/tooltip.tsx +61 -0
- package/renderer/hooks/use-code-copy.ts +88 -0
- package/renderer/hooks/use-openapi-title.ts +44 -0
- package/renderer/lib/api-docs/agent/index.ts +6 -0
- package/renderer/lib/api-docs/agent/indexer.ts +323 -0
- package/renderer/lib/api-docs/agent/spec-summary.ts +335 -0
- package/renderer/lib/api-docs/agent/types.ts +116 -0
- package/renderer/lib/api-docs/auth/auth-context.tsx +225 -0
- package/renderer/lib/api-docs/auth/auth-storage.ts +87 -0
- package/renderer/lib/api-docs/auth/crypto.ts +89 -0
- package/renderer/lib/api-docs/auth/index.ts +4 -0
- package/renderer/lib/api-docs/code-editor/db.ts +164 -0
- package/renderer/lib/api-docs/code-editor/hooks.ts +266 -0
- package/renderer/lib/api-docs/code-editor/index.ts +6 -0
- package/renderer/lib/api-docs/code-editor/mode-context.tsx +207 -0
- package/renderer/lib/api-docs/code-editor/types.ts +105 -0
- package/renderer/lib/api-docs/codegen/definitions.ts +297 -0
- package/renderer/lib/api-docs/codegen/har.ts +251 -0
- package/renderer/lib/api-docs/codegen/index.ts +159 -0
- package/renderer/lib/api-docs/factories.ts +151 -0
- package/renderer/lib/api-docs/index.ts +17 -0
- package/renderer/lib/api-docs/mobile-context.tsx +112 -0
- package/renderer/lib/api-docs/navigation-context.tsx +88 -0
- package/renderer/lib/api-docs/parsers/graphql/README.md +129 -0
- package/renderer/lib/api-docs/parsers/graphql/index.ts +91 -0
- package/renderer/lib/api-docs/parsers/graphql/parser.ts +491 -0
- package/renderer/lib/api-docs/parsers/graphql/transformer.ts +246 -0
- package/renderer/lib/api-docs/parsers/graphql/types.ts +283 -0
- package/renderer/lib/api-docs/parsers/openapi/README.md +32 -0
- package/renderer/lib/api-docs/parsers/openapi/dereferencer.ts +60 -0
- package/renderer/lib/api-docs/parsers/openapi/extractors/auth.ts +574 -0
- package/renderer/lib/api-docs/parsers/openapi/extractors/body.ts +403 -0
- package/renderer/lib/api-docs/parsers/openapi/extractors/index.ts +232 -0
- package/renderer/lib/api-docs/parsers/openapi/index.ts +171 -0
- package/renderer/lib/api-docs/parsers/openapi/transformer.ts +277 -0
- package/renderer/lib/api-docs/parsers/openapi/validator.ts +31 -0
- package/renderer/lib/api-docs/playground/context.tsx +107 -0
- package/renderer/lib/api-docs/playground/navigation-context.tsx +124 -0
- package/renderer/lib/api-docs/playground/request-builder.ts +223 -0
- package/renderer/lib/api-docs/playground/request-runner.ts +282 -0
- package/renderer/lib/api-docs/playground/types.ts +35 -0
- package/renderer/lib/api-docs/types.ts +269 -0
- package/renderer/lib/api-docs/utils.ts +311 -0
- package/renderer/lib/cache.ts +193 -0
- package/renderer/lib/docs/config/index.ts +29 -0
- package/renderer/lib/docs/config/loader.ts +142 -0
- package/renderer/lib/docs/config/schema.ts +298 -0
- package/renderer/lib/docs/index.ts +12 -0
- package/renderer/lib/docs/mdx/compiler.ts +176 -0
- package/renderer/lib/docs/mdx/frontmatter.ts +80 -0
- package/renderer/lib/docs/mdx/index.ts +26 -0
- package/renderer/lib/docs/navigation/generator.ts +348 -0
- package/renderer/lib/docs/navigation/index.ts +12 -0
- package/renderer/lib/docs/navigation/types.ts +123 -0
- package/renderer/lib/docs-navigation-context.tsx +80 -0
- package/renderer/lib/multi-tenant/context.ts +105 -0
- package/renderer/lib/storage/blob.ts +845 -0
- package/renderer/lib/utils.ts +6 -0
- package/renderer/next.config.ts +76 -0
- package/renderer/package.json +66 -0
- package/renderer/postcss.config.mjs +5 -0
- package/renderer/public/assets/images/screenshot.png +0 -0
- package/renderer/public/assets/logo/dark.svg +9 -0
- package/renderer/public/assets/logo/light.svg +9 -0
- package/renderer/public/assets/logo.svg +9 -0
- package/renderer/public/file.svg +1 -0
- package/renderer/public/globe.svg +1 -0
- package/renderer/public/icon.png +0 -0
- package/renderer/public/logo.svg +9 -0
- package/renderer/public/window.svg +1 -0
- package/renderer/tsconfig.json +28 -0
- package/templates/basic/README.md +139 -0
- package/templates/basic/assets/favicon.svg +4 -0
- package/templates/basic/assets/logo.svg +9 -0
- package/templates/basic/docs.json +47 -0
- package/templates/basic/guides/configuration.mdx +149 -0
- package/templates/basic/guides/overview.mdx +96 -0
- package/templates/basic/index.mdx +39 -0
- package/templates/basic/package.json +14 -0
- package/templates/basic/quickstart.mdx +92 -0
- package/templates/basic/vercel.json +6 -0
- package/templates/graphql/README.md +139 -0
- package/templates/graphql/api-reference/schema.graphql +305 -0
- package/templates/graphql/assets/favicon.svg +4 -0
- package/templates/graphql/assets/logo.svg +9 -0
- package/templates/graphql/docs.json +54 -0
- package/templates/graphql/guides/configuration.mdx +149 -0
- package/templates/graphql/guides/overview.mdx +96 -0
- package/templates/graphql/index.mdx +39 -0
- package/templates/graphql/package.json +14 -0
- package/templates/graphql/quickstart.mdx +92 -0
- package/templates/graphql/vercel.json +6 -0
- package/templates/openapi/README.md +139 -0
- package/templates/openapi/api-reference/openapi.json +419 -0
- package/templates/openapi/assets/favicon.svg +4 -0
- package/templates/openapi/assets/logo.svg +9 -0
- package/templates/openapi/docs.json +61 -0
- package/templates/openapi/guides/configuration.mdx +149 -0
- package/templates/openapi/guides/overview.mdx +96 -0
- package/templates/openapi/index.mdx +39 -0
- package/templates/openapi/package.json +14 -0
- package/templates/openapi/quickstart.mdx +92 -0
- package/templates/openapi/vercel.json +6 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { CaretDown, CaretRight } from '@phosphor-icons/react'
|
|
5
|
+
import { Badge } from '@/components/ui/badge'
|
|
6
|
+
import type { BrainfishRESTRequest } from '@/lib/api-docs/types'
|
|
7
|
+
import { MethodBadge } from '../shared/method-badge'
|
|
8
|
+
import { MarkdownRenderer } from '../shared/markdown-renderer'
|
|
9
|
+
import { CodeViewer } from '../playground/code-editor'
|
|
10
|
+
import { ResponseSchema } from '../shared/schema-viewer'
|
|
11
|
+
import { CodeSnippetCompact } from '../playground/code-snippet'
|
|
12
|
+
|
|
13
|
+
interface EndpointOptionsSidebarProps {
|
|
14
|
+
request: BrainfishRESTRequest
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface CollapsibleSectionProps {
|
|
18
|
+
title: string
|
|
19
|
+
children: React.ReactNode
|
|
20
|
+
defaultOpen?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function CollapsibleSection({ title, children, defaultOpen = true }: CollapsibleSectionProps) {
|
|
24
|
+
const [isOpen, setIsOpen] = useState(defaultOpen)
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className="border-b border-border">
|
|
28
|
+
<button
|
|
29
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
30
|
+
className="w-full flex items-center justify-between p-4 hover:bg-muted/50 transition-colors"
|
|
31
|
+
>
|
|
32
|
+
<span className="font-semibold text-sm">{title}</span>
|
|
33
|
+
{isOpen ? (
|
|
34
|
+
<CaretDown className="h-4 w-4 text-muted-foreground" weight="bold" />
|
|
35
|
+
) : (
|
|
36
|
+
<CaretRight className="h-4 w-4 text-muted-foreground" weight="bold" />
|
|
37
|
+
)}
|
|
38
|
+
</button>
|
|
39
|
+
{isOpen && <div className="px-4 pb-4">{children}</div>}
|
|
40
|
+
</div>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function EndpointOptionsSidebar({ request }: EndpointOptionsSidebarProps) {
|
|
45
|
+
return (
|
|
46
|
+
<div className="w-96 h-full border-l bg-muted/30 flex flex-col overflow-hidden">
|
|
47
|
+
{/* Header */}
|
|
48
|
+
<div className="p-4 border-b border-border bg-background">
|
|
49
|
+
<div className="flex items-center gap-2 mb-2">
|
|
50
|
+
<MethodBadge method={request.method} />
|
|
51
|
+
<h2 className="font-semibold text-lg truncate">{request.name}</h2>
|
|
52
|
+
</div>
|
|
53
|
+
<code className="text-xs text-muted-foreground font-mono break-all">
|
|
54
|
+
{request.endpoint}
|
|
55
|
+
</code>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
{/* Scrollable Content */}
|
|
59
|
+
<div className="flex-1 overflow-y-auto bg-background">
|
|
60
|
+
{/* Code Snippet - First Section */}
|
|
61
|
+
<CodeSnippetCompact request={request} />
|
|
62
|
+
|
|
63
|
+
{/* Description */}
|
|
64
|
+
{request.description && (
|
|
65
|
+
<CollapsibleSection title="Description" defaultOpen={true}>
|
|
66
|
+
<div className="text-sm text-muted-foreground">
|
|
67
|
+
<MarkdownRenderer content={request.description} />
|
|
68
|
+
</div>
|
|
69
|
+
</CollapsibleSection>
|
|
70
|
+
)}
|
|
71
|
+
|
|
72
|
+
{/* Parameters */}
|
|
73
|
+
{request.params.length > 0 && (
|
|
74
|
+
<CollapsibleSection title="Parameters" defaultOpen={false}>
|
|
75
|
+
<div className="border rounded-sm overflow-hidden">
|
|
76
|
+
<table className="w-full text-sm">
|
|
77
|
+
<thead className="bg-muted/50">
|
|
78
|
+
<tr>
|
|
79
|
+
<th className="px-3 py-2 text-left text-xs font-semibold">Name</th>
|
|
80
|
+
<th className="px-3 py-2 text-left text-xs font-semibold">Description</th>
|
|
81
|
+
<th className="px-3 py-2 text-left text-xs font-semibold">Required</th>
|
|
82
|
+
</tr>
|
|
83
|
+
</thead>
|
|
84
|
+
<tbody>
|
|
85
|
+
{request.params.map((param) => (
|
|
86
|
+
<tr key={param.key} className="border-t">
|
|
87
|
+
<td className="px-3 py-2 font-mono text-xs">{param.key}</td>
|
|
88
|
+
<td className="px-3 py-2 text-xs text-muted-foreground">
|
|
89
|
+
{param.description || '-'}
|
|
90
|
+
</td>
|
|
91
|
+
<td className="px-3 py-2 text-xs">
|
|
92
|
+
{param.active ? (
|
|
93
|
+
<Badge variant="default" className="text-xs">Required</Badge>
|
|
94
|
+
) : (
|
|
95
|
+
<span className="text-muted-foreground">Optional</span>
|
|
96
|
+
)}
|
|
97
|
+
</td>
|
|
98
|
+
</tr>
|
|
99
|
+
))}
|
|
100
|
+
</tbody>
|
|
101
|
+
</table>
|
|
102
|
+
</div>
|
|
103
|
+
</CollapsibleSection>
|
|
104
|
+
)}
|
|
105
|
+
|
|
106
|
+
{/* Headers */}
|
|
107
|
+
{request.headers.length > 0 && (
|
|
108
|
+
<CollapsibleSection title="Headers" defaultOpen={false}>
|
|
109
|
+
<div className="border rounded-sm overflow-hidden">
|
|
110
|
+
<table className="w-full text-sm">
|
|
111
|
+
<thead className="bg-muted/50">
|
|
112
|
+
<tr>
|
|
113
|
+
<th className="px-3 py-2 text-left text-xs font-semibold">Name</th>
|
|
114
|
+
<th className="px-3 py-2 text-left text-xs font-semibold">Description</th>
|
|
115
|
+
<th className="px-3 py-2 text-left text-xs font-semibold">Required</th>
|
|
116
|
+
</tr>
|
|
117
|
+
</thead>
|
|
118
|
+
<tbody>
|
|
119
|
+
{request.headers.map((header) => (
|
|
120
|
+
<tr key={header.key} className="border-t">
|
|
121
|
+
<td className="px-3 py-2 font-mono text-xs">{header.key}</td>
|
|
122
|
+
<td className="px-3 py-2 text-xs text-muted-foreground">
|
|
123
|
+
{header.description || '-'}
|
|
124
|
+
</td>
|
|
125
|
+
<td className="px-3 py-2 text-xs">
|
|
126
|
+
{header.active ? (
|
|
127
|
+
<Badge variant="default" className="text-xs">Required</Badge>
|
|
128
|
+
) : (
|
|
129
|
+
<span className="text-muted-foreground">Optional</span>
|
|
130
|
+
)}
|
|
131
|
+
</td>
|
|
132
|
+
</tr>
|
|
133
|
+
))}
|
|
134
|
+
</tbody>
|
|
135
|
+
</table>
|
|
136
|
+
</div>
|
|
137
|
+
</CollapsibleSection>
|
|
138
|
+
)}
|
|
139
|
+
|
|
140
|
+
{/* Authentication */}
|
|
141
|
+
{request.auth.authType !== 'none' && (
|
|
142
|
+
<CollapsibleSection title="Authentication" defaultOpen={false}>
|
|
143
|
+
<div className="border rounded-sm overflow-hidden">
|
|
144
|
+
{request.auth.authType === 'basic' && (
|
|
145
|
+
<>
|
|
146
|
+
<div className="px-3 py-2 border-b bg-muted/30">
|
|
147
|
+
<div className="flex items-center gap-2">
|
|
148
|
+
<span className="font-semibold text-sm">Basic Auth</span>
|
|
149
|
+
<span className="text-xs text-orange-500 font-medium">required</span>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
<div className="p-3 space-y-2">
|
|
153
|
+
<div className="flex items-center justify-between py-2 border-b border-border/50">
|
|
154
|
+
<span className="text-sm font-medium">username</span>
|
|
155
|
+
<span className="text-xs text-muted-foreground">string</span>
|
|
156
|
+
</div>
|
|
157
|
+
<div className="flex items-center justify-between py-2">
|
|
158
|
+
<span className="text-sm font-medium">password</span>
|
|
159
|
+
<span className="text-xs text-muted-foreground">string</span>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</>
|
|
163
|
+
)}
|
|
164
|
+
|
|
165
|
+
{request.auth.authType === 'bearer' && (
|
|
166
|
+
<>
|
|
167
|
+
<div className="px-3 py-2 border-b bg-muted/30">
|
|
168
|
+
<div className="flex items-center gap-2">
|
|
169
|
+
<span className="font-semibold text-sm">Bearer Token</span>
|
|
170
|
+
<span className="text-xs text-orange-500 font-medium">required</span>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
<div className="p-3">
|
|
174
|
+
<div className="flex items-center justify-between py-2">
|
|
175
|
+
<span className="text-sm font-medium">Authorization</span>
|
|
176
|
+
<span className="text-xs text-muted-foreground font-mono">Bearer <token></span>
|
|
177
|
+
</div>
|
|
178
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
179
|
+
Pass the token in the Authorization header
|
|
180
|
+
</p>
|
|
181
|
+
</div>
|
|
182
|
+
</>
|
|
183
|
+
)}
|
|
184
|
+
|
|
185
|
+
{request.auth.authType === 'api-key' && (
|
|
186
|
+
<>
|
|
187
|
+
<div className="px-3 py-2 border-b bg-muted/30">
|
|
188
|
+
<div className="flex items-center gap-2">
|
|
189
|
+
<span className="font-semibold text-sm">API Key</span>
|
|
190
|
+
<span className="text-xs text-orange-500 font-medium">required</span>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
<div className="p-3 space-y-3">
|
|
194
|
+
<div className="border-b border-border/50 pb-3">
|
|
195
|
+
<div className="flex items-start gap-2 flex-wrap">
|
|
196
|
+
<span className="font-semibold text-sm">{request.auth.key}</span>
|
|
197
|
+
<span className="text-xs text-muted-foreground">string</span>
|
|
198
|
+
<span className="text-xs text-orange-500 font-medium">required</span>
|
|
199
|
+
</div>
|
|
200
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
201
|
+
Your API key for authentication
|
|
202
|
+
</p>
|
|
203
|
+
</div>
|
|
204
|
+
<div className="flex items-center gap-2 text-xs">
|
|
205
|
+
<span className="text-muted-foreground">Location:</span>
|
|
206
|
+
<code className="bg-muted px-2 py-0.5 rounded font-mono">
|
|
207
|
+
{request.auth.addTo === 'HEADERS' ? 'Header' : 'Query'}
|
|
208
|
+
</code>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</>
|
|
212
|
+
)}
|
|
213
|
+
|
|
214
|
+
{request.auth.authType === 'oauth-2' && (
|
|
215
|
+
<>
|
|
216
|
+
<div className="px-3 py-2 border-b bg-muted/30">
|
|
217
|
+
<div className="flex items-center gap-2">
|
|
218
|
+
<span className="font-semibold text-sm">OAuth 2.0</span>
|
|
219
|
+
<span className="text-xs text-orange-500 font-medium">required</span>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
<div className="p-3 space-y-3">
|
|
223
|
+
<div className="border-b border-border/50 pb-3">
|
|
224
|
+
<div className="flex items-start gap-2 flex-wrap">
|
|
225
|
+
<span className="font-semibold text-sm">Grant Type</span>
|
|
226
|
+
<span className="text-xs text-muted-foreground">
|
|
227
|
+
{request.auth.grantTypeInfo.grantType.replace('_', ' ')}
|
|
228
|
+
</span>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
{request.auth.grantTypeInfo.authEndpoint && (
|
|
232
|
+
<div className="border-b border-border/50 pb-3">
|
|
233
|
+
<div className="flex items-start gap-2 flex-wrap">
|
|
234
|
+
<span className="font-semibold text-sm">authEndpoint</span>
|
|
235
|
+
<span className="text-xs text-muted-foreground">string</span>
|
|
236
|
+
</div>
|
|
237
|
+
<code className="text-xs text-muted-foreground mt-1 block break-all">
|
|
238
|
+
{request.auth.grantTypeInfo.authEndpoint}
|
|
239
|
+
</code>
|
|
240
|
+
</div>
|
|
241
|
+
)}
|
|
242
|
+
{request.auth.grantTypeInfo.tokenEndpoint && (
|
|
243
|
+
<div className="pb-1">
|
|
244
|
+
<div className="flex items-start gap-2 flex-wrap">
|
|
245
|
+
<span className="font-semibold text-sm">tokenEndpoint</span>
|
|
246
|
+
<span className="text-xs text-muted-foreground">string</span>
|
|
247
|
+
</div>
|
|
248
|
+
<code className="text-xs text-muted-foreground mt-1 block break-all">
|
|
249
|
+
{request.auth.grantTypeInfo.tokenEndpoint}
|
|
250
|
+
</code>
|
|
251
|
+
</div>
|
|
252
|
+
)}
|
|
253
|
+
</div>
|
|
254
|
+
</>
|
|
255
|
+
)}
|
|
256
|
+
</div>
|
|
257
|
+
</CollapsibleSection>
|
|
258
|
+
)}
|
|
259
|
+
|
|
260
|
+
{/* Body */}
|
|
261
|
+
{request.body.body !== null && (
|
|
262
|
+
<CollapsibleSection title="Request Body" defaultOpen={false}>
|
|
263
|
+
<div className="border rounded-sm overflow-hidden">
|
|
264
|
+
<div className="bg-muted/50 px-3 py-2 border-b">
|
|
265
|
+
<span className="text-xs font-medium">Content-Type: </span>
|
|
266
|
+
<code className="text-xs">{request.body.contentType}</code>
|
|
267
|
+
</div>
|
|
268
|
+
{Array.isArray(request.body.body) ? (
|
|
269
|
+
<div className="p-3 space-y-1">
|
|
270
|
+
{request.body.body.map((item, index) => (
|
|
271
|
+
<div key={index} className="flex items-center gap-2 text-xs">
|
|
272
|
+
<code className="bg-background px-2 py-1 rounded">{item.key}</code>
|
|
273
|
+
<span className="text-muted-foreground">
|
|
274
|
+
{item.isFile ? '(file)' : '(text)'}
|
|
275
|
+
</span>
|
|
276
|
+
</div>
|
|
277
|
+
))}
|
|
278
|
+
</div>
|
|
279
|
+
) : (
|
|
280
|
+
<div className="dark-editor">
|
|
281
|
+
<CodeViewer
|
|
282
|
+
value={typeof request.body.body === 'string' ? request.body.body : JSON.stringify(request.body.body)}
|
|
283
|
+
language={request.body.contentType?.includes('json') ? 'json' : request.body.contentType?.includes('xml') ? 'xml' : 'plaintext'}
|
|
284
|
+
theme="dark"
|
|
285
|
+
height="auto"
|
|
286
|
+
minHeight={80}
|
|
287
|
+
maxHeight={250}
|
|
288
|
+
showBorder={false}
|
|
289
|
+
rounded={false}
|
|
290
|
+
/>
|
|
291
|
+
</div>
|
|
292
|
+
)}
|
|
293
|
+
</div>
|
|
294
|
+
</CollapsibleSection>
|
|
295
|
+
)}
|
|
296
|
+
|
|
297
|
+
{/* Responses */}
|
|
298
|
+
{Object.keys(request.responses).length > 0 && (
|
|
299
|
+
<CollapsibleSection title="Responses" defaultOpen={true}>
|
|
300
|
+
<div>
|
|
301
|
+
{Object.entries(request.responses).map(([name, response]) => (
|
|
302
|
+
<ResponseSchema
|
|
303
|
+
key={name}
|
|
304
|
+
code={response.code}
|
|
305
|
+
status={response.status}
|
|
306
|
+
description={name !== response.status ? name : undefined}
|
|
307
|
+
schema={response.body}
|
|
308
|
+
/>
|
|
309
|
+
))}
|
|
310
|
+
</div>
|
|
311
|
+
</CollapsibleSection>
|
|
312
|
+
)}
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
)
|
|
316
|
+
}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react'
|
|
4
|
+
import { X, Spinner } from '@phosphor-icons/react'
|
|
5
|
+
import { Button } from '@/components/ui/button'
|
|
6
|
+
import {
|
|
7
|
+
Select,
|
|
8
|
+
SelectContent,
|
|
9
|
+
SelectItem,
|
|
10
|
+
SelectTrigger,
|
|
11
|
+
SelectValue,
|
|
12
|
+
} from '@/components/ui/select'
|
|
13
|
+
import type { BrainfishCollection, BrainfishRESTRequest, BrainfishDocPage } from '@/lib/api-docs/types'
|
|
14
|
+
import { CollectionTree } from './collection-tree'
|
|
15
|
+
import { SidebarSection } from './sidebar-section'
|
|
16
|
+
import { SidebarItem, SlidingIndicatorProvider } from './sidebar-item'
|
|
17
|
+
import { SidebarGroup } from './sidebar-group'
|
|
18
|
+
import { extractMarkdownHeadings, type MarkdownHeading } from '@/lib/api-docs/utils'
|
|
19
|
+
import { useMobile } from '@/lib/api-docs/mobile-context'
|
|
20
|
+
import { cn } from '@/lib/utils'
|
|
21
|
+
|
|
22
|
+
interface ApiVersion {
|
|
23
|
+
version: string
|
|
24
|
+
spec: string
|
|
25
|
+
default: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface ApiDocsSidebarProps {
|
|
29
|
+
collection: BrainfishCollection
|
|
30
|
+
selectedRequest: BrainfishRESTRequest | null
|
|
31
|
+
selectedDocSection: string | null
|
|
32
|
+
selectedDocPage?: string | null
|
|
33
|
+
activeTab?: string
|
|
34
|
+
onSelectRequest: (request: BrainfishRESTRequest) => void
|
|
35
|
+
onSelectDocumentation?: (headingId: string) => void
|
|
36
|
+
onSelectDocPage?: (slug: string) => void
|
|
37
|
+
apiVersions?: ApiVersion[]
|
|
38
|
+
selectedApiVersion?: string | null
|
|
39
|
+
onApiVersionChange?: (version: string) => void
|
|
40
|
+
isVersionLoading?: boolean
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function DocsSidebar({
|
|
44
|
+
collection,
|
|
45
|
+
selectedRequest,
|
|
46
|
+
selectedDocSection,
|
|
47
|
+
selectedDocPage,
|
|
48
|
+
activeTab,
|
|
49
|
+
onSelectRequest,
|
|
50
|
+
onSelectDocumentation,
|
|
51
|
+
onSelectDocPage,
|
|
52
|
+
apiVersions,
|
|
53
|
+
selectedApiVersion,
|
|
54
|
+
onApiVersionChange,
|
|
55
|
+
isVersionLoading,
|
|
56
|
+
}: ApiDocsSidebarProps) {
|
|
57
|
+
const { isMobile, isLeftSidebarOpen, closeLeftSidebar } = useMobile()
|
|
58
|
+
|
|
59
|
+
// Handle request selection with mobile close
|
|
60
|
+
const handleSelectRequest = (request: BrainfishRESTRequest) => {
|
|
61
|
+
onSelectRequest(request)
|
|
62
|
+
if (isMobile) {
|
|
63
|
+
closeLeftSidebar()
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Handle doc click with mobile close
|
|
68
|
+
const handleDocClickWithClose = (headingId: string) => {
|
|
69
|
+
if (onSelectDocumentation) {
|
|
70
|
+
onSelectDocumentation(headingId)
|
|
71
|
+
}
|
|
72
|
+
if (isMobile) {
|
|
73
|
+
closeLeftSidebar()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Handle doc page click with mobile close
|
|
78
|
+
const handleDocPageClick = (slug: string) => {
|
|
79
|
+
if (onSelectDocPage) {
|
|
80
|
+
onSelectDocPage(slug)
|
|
81
|
+
}
|
|
82
|
+
if (isMobile) {
|
|
83
|
+
closeLeftSidebar()
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Extract documentation headings from collection description
|
|
88
|
+
const docHeadings = useMemo(() => {
|
|
89
|
+
return collection.description
|
|
90
|
+
? extractMarkdownHeadings(collection.description)
|
|
91
|
+
: []
|
|
92
|
+
}, [collection.description])
|
|
93
|
+
|
|
94
|
+
// Get doc groups from collection
|
|
95
|
+
const docGroups = collection.docGroups || []
|
|
96
|
+
|
|
97
|
+
const handleDocClick = (headingId: string) => {
|
|
98
|
+
handleDocClickWithClose(headingId)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Determine if Introduction should be highlighted
|
|
102
|
+
const isIntroductionSelected = !selectedRequest && !selectedDocSection && !selectedDocPage
|
|
103
|
+
|
|
104
|
+
// Check if a heading or any of its children is selected
|
|
105
|
+
const isHeadingSelected = (heading: MarkdownHeading): boolean => {
|
|
106
|
+
if (selectedDocSection === heading.id) return true
|
|
107
|
+
if (heading.children?.some(child => selectedDocSection === child.id)) return true
|
|
108
|
+
return false
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Get the selected child heading ID for a heading group
|
|
112
|
+
const getSelectedChildHeading = (heading: MarkdownHeading): string | null => {
|
|
113
|
+
if (selectedDocSection === heading.id) return heading.id
|
|
114
|
+
const selected = heading.children?.find(child => selectedDocSection === child.id)
|
|
115
|
+
return selected?.id || null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Render a documentation heading item (may have children)
|
|
119
|
+
const renderHeading = (heading: MarkdownHeading, index: number) => {
|
|
120
|
+
const hasChildren = heading.children && heading.children.length > 0
|
|
121
|
+
const isSelected = selectedDocSection === heading.id
|
|
122
|
+
|
|
123
|
+
// If heading is "Introduction", handle it specially
|
|
124
|
+
if (heading.text === 'Introduction') {
|
|
125
|
+
return (
|
|
126
|
+
<SidebarItem
|
|
127
|
+
key={`${heading.id}-${index}`}
|
|
128
|
+
itemId={`heading-introduction`}
|
|
129
|
+
selected={isIntroductionSelected}
|
|
130
|
+
onClick={() => handleDocClick('introduction')}
|
|
131
|
+
>
|
|
132
|
+
Introduction
|
|
133
|
+
</SidebarItem>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (hasChildren) {
|
|
138
|
+
return (
|
|
139
|
+
<SidebarGroup
|
|
140
|
+
key={`${heading.id}-${index}`}
|
|
141
|
+
title={heading.text}
|
|
142
|
+
defaultOpen={isHeadingSelected(heading)}
|
|
143
|
+
selected={isSelected}
|
|
144
|
+
selectedChildSlug={getSelectedChildHeading(heading)}
|
|
145
|
+
onClick={() => handleDocClick(heading.id)}
|
|
146
|
+
>
|
|
147
|
+
{heading.children!.map((child, childIndex) => (
|
|
148
|
+
<SidebarItem
|
|
149
|
+
key={`${child.id}-${childIndex}`}
|
|
150
|
+
itemId={`heading-${child.id}`}
|
|
151
|
+
selected={selectedDocSection === child.id}
|
|
152
|
+
indent={1}
|
|
153
|
+
onClick={() => handleDocClick(child.id)}
|
|
154
|
+
>
|
|
155
|
+
{child.text}
|
|
156
|
+
</SidebarItem>
|
|
157
|
+
))}
|
|
158
|
+
</SidebarGroup>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<SidebarItem
|
|
164
|
+
key={`${heading.id}-${index}`}
|
|
165
|
+
itemId={`heading-${heading.id}`}
|
|
166
|
+
selected={isSelected}
|
|
167
|
+
onClick={() => handleDocClick(heading.id)}
|
|
168
|
+
>
|
|
169
|
+
{heading.text}
|
|
170
|
+
</SidebarItem>
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Check if any child in a group is selected
|
|
175
|
+
const isChildSelected = (page: BrainfishDocPage): boolean => {
|
|
176
|
+
if (!page.children) return false
|
|
177
|
+
return page.children.some(child => selectedDocPage === child.slug)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Get the selected child slug for a group (for triggering auto-expand)
|
|
181
|
+
const getSelectedChildSlug = (page: BrainfishDocPage): string | null => {
|
|
182
|
+
if (!page.children) return null
|
|
183
|
+
const selected = page.children.find(child => selectedDocPage === child.slug)
|
|
184
|
+
return selected?.slug || null
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Render a doc page item (may be a collapsible group with children)
|
|
188
|
+
const renderDocPage = (page: BrainfishDocPage) => {
|
|
189
|
+
// Handle collapsible group with children
|
|
190
|
+
if (page.isGroup && page.children && page.children.length > 0) {
|
|
191
|
+
return (
|
|
192
|
+
<SidebarGroup
|
|
193
|
+
key={page.id}
|
|
194
|
+
title={page.title}
|
|
195
|
+
defaultOpen={isChildSelected(page)}
|
|
196
|
+
selectedChildSlug={getSelectedChildSlug(page)}
|
|
197
|
+
>
|
|
198
|
+
{page.children.map(child => {
|
|
199
|
+
const isSelected = selectedDocPage === child.slug
|
|
200
|
+
return (
|
|
201
|
+
<SidebarItem
|
|
202
|
+
key={child.id}
|
|
203
|
+
itemId={`doc-${child.slug}`}
|
|
204
|
+
selected={isSelected}
|
|
205
|
+
indent={1}
|
|
206
|
+
icon={child.icon}
|
|
207
|
+
onClick={() => handleDocPageClick(child.slug)}
|
|
208
|
+
>
|
|
209
|
+
{child.title}
|
|
210
|
+
</SidebarItem>
|
|
211
|
+
)
|
|
212
|
+
})}
|
|
213
|
+
</SidebarGroup>
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Regular page item
|
|
218
|
+
const isSelected = selectedDocPage === page.slug
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<SidebarItem
|
|
222
|
+
key={page.id}
|
|
223
|
+
itemId={`doc-${page.slug}`}
|
|
224
|
+
selected={isSelected}
|
|
225
|
+
icon={page.icon}
|
|
226
|
+
onClick={() => handleDocPageClick(page.slug)}
|
|
227
|
+
>
|
|
228
|
+
{page.title}
|
|
229
|
+
</SidebarItem>
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
<>
|
|
235
|
+
{/* Mobile overlay backdrop */}
|
|
236
|
+
{isMobile && isLeftSidebarOpen && (
|
|
237
|
+
<div
|
|
238
|
+
className="docs-sidebar-overlay fixed inset-0 bg-black/50 z-40 lg:hidden"
|
|
239
|
+
onClick={closeLeftSidebar}
|
|
240
|
+
/>
|
|
241
|
+
)}
|
|
242
|
+
|
|
243
|
+
<aside
|
|
244
|
+
className={cn(
|
|
245
|
+
"docs-sidebar flex flex-col border-r border-sidebar-border overflow-hidden bg-muted dark:bg-background",
|
|
246
|
+
// Desktop: always visible, fixed width
|
|
247
|
+
"lg:relative lg:w-72 lg:h-full",
|
|
248
|
+
// Mobile: drawer behavior
|
|
249
|
+
"fixed inset-y-0 left-0 z-50 w-[280px] h-full",
|
|
250
|
+
"transform transition-transform duration-300 ease-in-out",
|
|
251
|
+
"lg:transform-none lg:translate-x-0",
|
|
252
|
+
isMobile && !isLeftSidebarOpen && "-translate-x-full",
|
|
253
|
+
isMobile && isLeftSidebarOpen && "translate-x-0"
|
|
254
|
+
)}
|
|
255
|
+
>
|
|
256
|
+
{/* Mobile close button */}
|
|
257
|
+
{isMobile && (
|
|
258
|
+
<div className="px-4 h-[41px] flex items-center justify-end border-b border-sidebar-border shrink-0">
|
|
259
|
+
<Button
|
|
260
|
+
variant="ghost"
|
|
261
|
+
size="icon"
|
|
262
|
+
onClick={closeLeftSidebar}
|
|
263
|
+
className="h-7 w-7 lg:hidden"
|
|
264
|
+
>
|
|
265
|
+
<X className="h-4 w-4" weight="bold" />
|
|
266
|
+
</Button>
|
|
267
|
+
</div>
|
|
268
|
+
)}
|
|
269
|
+
|
|
270
|
+
{/* API Version Selector - Only show when multiple versions exist and on API Reference tab */}
|
|
271
|
+
{activeTab === 'api-reference' && apiVersions && apiVersions.length > 1 && (
|
|
272
|
+
<div className="docs-sidebar-version-selector px-4 py-3 border-b border-sidebar-border shrink-0">
|
|
273
|
+
<div className="flex items-center justify-between mb-1.5">
|
|
274
|
+
<label className="docs-sidebar-version-label text-xs font-medium text-muted-foreground">
|
|
275
|
+
API Version
|
|
276
|
+
</label>
|
|
277
|
+
{isVersionLoading && (
|
|
278
|
+
<Spinner className="h-3 w-3 animate-spin text-muted-foreground" />
|
|
279
|
+
)}
|
|
280
|
+
</div>
|
|
281
|
+
<Select
|
|
282
|
+
value={selectedApiVersion || apiVersions.find(v => v.default)?.version || apiVersions[0]?.version}
|
|
283
|
+
onValueChange={(value) => onApiVersionChange?.(value)}
|
|
284
|
+
disabled={isVersionLoading}
|
|
285
|
+
>
|
|
286
|
+
<SelectTrigger className={cn("docs-sidebar-version-trigger w-full h-9 text-sm", isVersionLoading && "opacity-50")}>
|
|
287
|
+
<SelectValue placeholder="Select version" />
|
|
288
|
+
</SelectTrigger>
|
|
289
|
+
<SelectContent>
|
|
290
|
+
{apiVersions.map((v) => (
|
|
291
|
+
<SelectItem key={v.version} value={v.version}>
|
|
292
|
+
{v.version}
|
|
293
|
+
{v.default && <span className="ml-2 text-xs text-muted-foreground">(latest)</span>}
|
|
294
|
+
</SelectItem>
|
|
295
|
+
))}
|
|
296
|
+
</SelectContent>
|
|
297
|
+
</Select>
|
|
298
|
+
</div>
|
|
299
|
+
)}
|
|
300
|
+
|
|
301
|
+
{/* Scrollable content with sliding indicator */}
|
|
302
|
+
<SlidingIndicatorProvider className="docs-sidebar-content flex-1 overflow-y-auto overflow-x-hidden custom-scroll">
|
|
303
|
+
{/* Documentation Section (from description headings) - Only shown in API Reference tab */}
|
|
304
|
+
{activeTab === 'api-reference' && docHeadings.length > 0 && (
|
|
305
|
+
<SidebarSection title="Documentation">
|
|
306
|
+
{docHeadings.map((heading, index) => renderHeading(heading, index))}
|
|
307
|
+
</SidebarSection>
|
|
308
|
+
)}
|
|
309
|
+
|
|
310
|
+
{/* Documentation Pages Section (from docs.json) */}
|
|
311
|
+
{docGroups.length > 0 && (
|
|
312
|
+
<>
|
|
313
|
+
{docGroups.map((group, index) => (
|
|
314
|
+
<SidebarSection
|
|
315
|
+
key={group.id}
|
|
316
|
+
title={group.title}
|
|
317
|
+
icon={group.icon}
|
|
318
|
+
className={index > 0 || (activeTab === 'api-reference' && docHeadings.length > 0) ? '' : ''}
|
|
319
|
+
>
|
|
320
|
+
{group.pages.map(renderDocPage)}
|
|
321
|
+
</SidebarSection>
|
|
322
|
+
))}
|
|
323
|
+
</>
|
|
324
|
+
)}
|
|
325
|
+
|
|
326
|
+
{/* Endpoints Section */}
|
|
327
|
+
{(collection.folders.length > 0 || collection.requests.length > 0) && (
|
|
328
|
+
<SidebarSection
|
|
329
|
+
title="Endpoints"
|
|
330
|
+
className={((activeTab === 'api-reference' && docHeadings.length > 0) || docGroups.length > 0) ? 'border-t border-sidebar-border' : ''}
|
|
331
|
+
>
|
|
332
|
+
<CollectionTree
|
|
333
|
+
collection={collection}
|
|
334
|
+
selectedRequest={selectedRequest}
|
|
335
|
+
onSelectRequest={handleSelectRequest}
|
|
336
|
+
/>
|
|
337
|
+
</SidebarSection>
|
|
338
|
+
)}
|
|
339
|
+
</SlidingIndicatorProvider>
|
|
340
|
+
</aside>
|
|
341
|
+
</>
|
|
342
|
+
)
|
|
343
|
+
}
|