@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,280 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { ShieldCheck, Key, Lock, User, Hash, WarningCircle } from '@phosphor-icons/react'
|
|
4
|
+
import {
|
|
5
|
+
Select,
|
|
6
|
+
SelectContent,
|
|
7
|
+
SelectItem,
|
|
8
|
+
SelectTrigger,
|
|
9
|
+
SelectValue,
|
|
10
|
+
} from '@/components/ui/select'
|
|
11
|
+
import { Input } from '@/components/ui/input'
|
|
12
|
+
import type { BrainfishRESTAuth } from '@/lib/api-docs/types'
|
|
13
|
+
import { cn } from '@/lib/utils'
|
|
14
|
+
import { usePlaygroundNavigation } from '@/lib/api-docs/playground/navigation-context'
|
|
15
|
+
|
|
16
|
+
const AUTH_TYPES = [
|
|
17
|
+
{ value: 'none', label: 'No Auth', icon: ShieldCheck },
|
|
18
|
+
{ value: 'basic', label: 'Basic Auth', icon: User },
|
|
19
|
+
{ value: 'bearer', label: 'Bearer Token', icon: Key },
|
|
20
|
+
{ value: 'api-key', label: 'API Key', icon: Lock },
|
|
21
|
+
] as const
|
|
22
|
+
|
|
23
|
+
interface AuthEditorProps {
|
|
24
|
+
auth: BrainfishRESTAuth
|
|
25
|
+
onChange: (auth: BrainfishRESTAuth) => void
|
|
26
|
+
/** Show error styling to indicate this section needs attention */
|
|
27
|
+
showError?: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function AuthEditor({ auth, onChange, showError = false }: AuthEditorProps) {
|
|
31
|
+
const { state: navState } = usePlaygroundNavigation()
|
|
32
|
+
|
|
33
|
+
// Check if auth field should be highlighted (from either prop or context)
|
|
34
|
+
const isHighlighted = showError || (navState.highlightField?.type === 'auth' && navState.showError)
|
|
35
|
+
const handleTypeChange = (type: string) => {
|
|
36
|
+
switch (type) {
|
|
37
|
+
case 'none':
|
|
38
|
+
onChange({ authType: 'none', authActive: true })
|
|
39
|
+
break
|
|
40
|
+
case 'basic':
|
|
41
|
+
onChange({ authType: 'basic', authActive: true, username: '', password: '' })
|
|
42
|
+
break
|
|
43
|
+
case 'bearer':
|
|
44
|
+
onChange({ authType: 'bearer', authActive: true, token: '' })
|
|
45
|
+
break
|
|
46
|
+
case 'api-key':
|
|
47
|
+
onChange({ authType: 'api-key', authActive: true, addTo: 'HEADERS', key: 'X-API-Key', value: '' })
|
|
48
|
+
break
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const currentType = AUTH_TYPES.find((t) => t.value === auth.authType) || AUTH_TYPES[0]
|
|
53
|
+
const CurrentIcon = currentType.icon
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div className="flex flex-col h-full">
|
|
57
|
+
{/* Header with Auth Type Selector */}
|
|
58
|
+
<div className={cn(
|
|
59
|
+
"flex items-center border-b bg-muted/30 px-4 py-2",
|
|
60
|
+
isHighlighted ? "border-red-500 bg-red-500/5" : "border-border"
|
|
61
|
+
)}>
|
|
62
|
+
{isHighlighted && (
|
|
63
|
+
<WarningCircle className="h-4 w-4 text-red-500 animate-pulse mr-2" weight="fill" />
|
|
64
|
+
)}
|
|
65
|
+
<span className={cn(
|
|
66
|
+
"text-xs font-medium mr-3",
|
|
67
|
+
isHighlighted ? "text-red-500" : "text-muted-foreground"
|
|
68
|
+
)}>
|
|
69
|
+
{isHighlighted ? "Auth required" : "Type"}
|
|
70
|
+
</span>
|
|
71
|
+
<Select value={auth.authType} onValueChange={handleTypeChange}>
|
|
72
|
+
<SelectTrigger className="h-8 w-auto min-w-[140px] text-sm border-0 bg-transparent hover:bg-muted/50">
|
|
73
|
+
<SelectValue>
|
|
74
|
+
<span className="flex items-center gap-2">
|
|
75
|
+
<CurrentIcon className="h-3.5 w-3.5" weight="bold" />
|
|
76
|
+
<span>{currentType.label}</span>
|
|
77
|
+
</span>
|
|
78
|
+
</SelectValue>
|
|
79
|
+
</SelectTrigger>
|
|
80
|
+
<SelectContent>
|
|
81
|
+
{AUTH_TYPES.map((type) => (
|
|
82
|
+
<SelectItem key={type.value} value={type.value}>
|
|
83
|
+
<div className="flex items-center gap-2">
|
|
84
|
+
<type.icon className="h-3.5 w-3.5" weight="bold" />
|
|
85
|
+
<span>{type.label}</span>
|
|
86
|
+
</div>
|
|
87
|
+
</SelectItem>
|
|
88
|
+
))}
|
|
89
|
+
</SelectContent>
|
|
90
|
+
</Select>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
{/* Auth Content */}
|
|
94
|
+
<div className="flex-1 overflow-auto">
|
|
95
|
+
{auth.authType === 'none' && (
|
|
96
|
+
<div className={cn(
|
|
97
|
+
"flex flex-col items-center justify-center py-12 px-6 text-center",
|
|
98
|
+
isHighlighted && "bg-red-500/5 border-2 border-red-500/30 rounded-lg m-4 animate-pulse"
|
|
99
|
+
)}>
|
|
100
|
+
{isHighlighted ? (
|
|
101
|
+
<>
|
|
102
|
+
<WarningCircle className="h-10 w-10 text-red-500 mb-3" weight="fill" />
|
|
103
|
+
<p className="text-sm font-medium text-red-600 dark:text-red-400 mb-1">
|
|
104
|
+
Authentication Required
|
|
105
|
+
</p>
|
|
106
|
+
<p className="text-xs text-muted-foreground">
|
|
107
|
+
Select an auth type above to configure credentials
|
|
108
|
+
</p>
|
|
109
|
+
</>
|
|
110
|
+
) : (
|
|
111
|
+
<>
|
|
112
|
+
<ShieldCheck className="h-10 w-10 text-muted-foreground/50 mb-3" weight="duotone" />
|
|
113
|
+
<p className="text-sm text-muted-foreground">
|
|
114
|
+
No authentication required
|
|
115
|
+
</p>
|
|
116
|
+
</>
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
)}
|
|
120
|
+
|
|
121
|
+
{auth.authType === 'basic' && (
|
|
122
|
+
<table className="w-full text-sm">
|
|
123
|
+
<thead className="bg-muted/20 text-xs text-muted-foreground">
|
|
124
|
+
<tr>
|
|
125
|
+
<th className="text-left font-medium px-4 py-2 w-32">Field</th>
|
|
126
|
+
<th className="text-left font-medium px-4 py-2">Value</th>
|
|
127
|
+
</tr>
|
|
128
|
+
</thead>
|
|
129
|
+
<tbody>
|
|
130
|
+
<tr className="border-b border-border/50">
|
|
131
|
+
<td className="px-4 py-1">
|
|
132
|
+
<div className="flex items-center gap-2 text-sm font-medium">
|
|
133
|
+
<User className="h-3.5 w-3.5 text-muted-foreground" />
|
|
134
|
+
username
|
|
135
|
+
</div>
|
|
136
|
+
</td>
|
|
137
|
+
<td className="px-2 py-1">
|
|
138
|
+
<Input
|
|
139
|
+
type="text"
|
|
140
|
+
value={auth.username}
|
|
141
|
+
onChange={(e) => onChange({ ...auth, username: e.target.value })}
|
|
142
|
+
placeholder="Enter username..."
|
|
143
|
+
className="h-9 border-border/50 bg-transparent focus-visible:ring-1 focus-visible:ring-ring"
|
|
144
|
+
/>
|
|
145
|
+
</td>
|
|
146
|
+
</tr>
|
|
147
|
+
<tr className="border-b border-border/50">
|
|
148
|
+
<td className="px-4 py-1">
|
|
149
|
+
<div className="flex items-center gap-2 text-sm font-medium">
|
|
150
|
+
<Lock className="h-3.5 w-3.5 text-muted-foreground" />
|
|
151
|
+
password
|
|
152
|
+
</div>
|
|
153
|
+
</td>
|
|
154
|
+
<td className="px-2 py-1">
|
|
155
|
+
<Input
|
|
156
|
+
type="password"
|
|
157
|
+
value={auth.password}
|
|
158
|
+
onChange={(e) => onChange({ ...auth, password: e.target.value })}
|
|
159
|
+
placeholder="Enter password..."
|
|
160
|
+
className="h-9 border-border/50 bg-transparent focus-visible:ring-1 focus-visible:ring-ring"
|
|
161
|
+
/>
|
|
162
|
+
</td>
|
|
163
|
+
</tr>
|
|
164
|
+
</tbody>
|
|
165
|
+
</table>
|
|
166
|
+
)}
|
|
167
|
+
|
|
168
|
+
{auth.authType === 'bearer' && (
|
|
169
|
+
<table className="w-full text-sm">
|
|
170
|
+
<thead className="bg-muted/20 text-xs text-muted-foreground">
|
|
171
|
+
<tr>
|
|
172
|
+
<th className="text-left font-medium px-4 py-2 w-32">Field</th>
|
|
173
|
+
<th className="text-left font-medium px-4 py-2">Value</th>
|
|
174
|
+
</tr>
|
|
175
|
+
</thead>
|
|
176
|
+
<tbody>
|
|
177
|
+
<tr className="border-b border-border/50">
|
|
178
|
+
<td className="px-4 py-1 align-top pt-3">
|
|
179
|
+
<div className="flex items-center gap-2 text-sm font-medium">
|
|
180
|
+
<Key className="h-3.5 w-3.5 text-muted-foreground" />
|
|
181
|
+
token
|
|
182
|
+
</div>
|
|
183
|
+
</td>
|
|
184
|
+
<td className="px-2 py-1">
|
|
185
|
+
<textarea
|
|
186
|
+
value={auth.token}
|
|
187
|
+
onChange={(e) => onChange({ ...auth, token: e.target.value })}
|
|
188
|
+
placeholder="Paste your bearer token..."
|
|
189
|
+
className="w-full min-h-[80px] px-3 py-2 rounded-md border border-border/50 bg-transparent font-mono text-sm resize-none focus:outline-none focus:ring-1 focus:ring-ring placeholder:text-muted-foreground/50"
|
|
190
|
+
/>
|
|
191
|
+
</td>
|
|
192
|
+
</tr>
|
|
193
|
+
</tbody>
|
|
194
|
+
</table>
|
|
195
|
+
)}
|
|
196
|
+
|
|
197
|
+
{auth.authType === 'api-key' && (
|
|
198
|
+
<>
|
|
199
|
+
{/* Location toggle */}
|
|
200
|
+
<div className="flex items-center gap-3 px-4 py-3 border-b border-border/50 bg-muted/10">
|
|
201
|
+
<span className="text-xs text-muted-foreground font-medium">Add to</span>
|
|
202
|
+
<div className="flex p-0.5 bg-muted rounded-sm">
|
|
203
|
+
<button
|
|
204
|
+
type="button"
|
|
205
|
+
onClick={() => onChange({ ...auth, addTo: 'HEADERS' })}
|
|
206
|
+
className={cn(
|
|
207
|
+
"px-3 py-1 rounded text-xs font-medium transition-all",
|
|
208
|
+
auth.addTo === 'HEADERS'
|
|
209
|
+
? "bg-background text-foreground shadow-sm"
|
|
210
|
+
: "text-muted-foreground hover:text-foreground"
|
|
211
|
+
)}
|
|
212
|
+
>
|
|
213
|
+
Header
|
|
214
|
+
</button>
|
|
215
|
+
<button
|
|
216
|
+
type="button"
|
|
217
|
+
onClick={() => onChange({ ...auth, addTo: 'QUERY_PARAMS' })}
|
|
218
|
+
className={cn(
|
|
219
|
+
"px-3 py-1 rounded text-xs font-medium transition-all",
|
|
220
|
+
auth.addTo === 'QUERY_PARAMS'
|
|
221
|
+
? "bg-background text-foreground shadow-sm"
|
|
222
|
+
: "text-muted-foreground hover:text-foreground"
|
|
223
|
+
)}
|
|
224
|
+
>
|
|
225
|
+
Query
|
|
226
|
+
</button>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
{/* Key-value inputs */}
|
|
231
|
+
<table className="w-full text-sm">
|
|
232
|
+
<thead className="bg-muted/20 text-xs text-muted-foreground">
|
|
233
|
+
<tr>
|
|
234
|
+
<th className="text-left font-medium px-4 py-2 w-32">Field</th>
|
|
235
|
+
<th className="text-left font-medium px-4 py-2">Value</th>
|
|
236
|
+
</tr>
|
|
237
|
+
</thead>
|
|
238
|
+
<tbody>
|
|
239
|
+
<tr className="border-b border-border/50">
|
|
240
|
+
<td className="px-4 py-1">
|
|
241
|
+
<div className="flex items-center gap-2 text-sm font-medium">
|
|
242
|
+
<Hash className="h-3.5 w-3.5 text-muted-foreground" />
|
|
243
|
+
key
|
|
244
|
+
</div>
|
|
245
|
+
</td>
|
|
246
|
+
<td className="px-2 py-1">
|
|
247
|
+
<Input
|
|
248
|
+
type="text"
|
|
249
|
+
value={auth.key}
|
|
250
|
+
onChange={(e) => onChange({ ...auth, key: e.target.value })}
|
|
251
|
+
placeholder="X-API-Key"
|
|
252
|
+
className="h-9 border-border/50 bg-transparent focus-visible:ring-1 focus-visible:ring-ring font-mono"
|
|
253
|
+
/>
|
|
254
|
+
</td>
|
|
255
|
+
</tr>
|
|
256
|
+
<tr className="border-b border-border/50">
|
|
257
|
+
<td className="px-4 py-1">
|
|
258
|
+
<div className="flex items-center gap-2 text-sm font-medium">
|
|
259
|
+
<Key className="h-3.5 w-3.5 text-muted-foreground" />
|
|
260
|
+
value
|
|
261
|
+
</div>
|
|
262
|
+
</td>
|
|
263
|
+
<td className="px-2 py-1">
|
|
264
|
+
<Input
|
|
265
|
+
type="password"
|
|
266
|
+
value={auth.value}
|
|
267
|
+
onChange={(e) => onChange({ ...auth, value: e.target.value })}
|
|
268
|
+
placeholder="Enter API key..."
|
|
269
|
+
className="h-9 border-border/50 bg-transparent focus-visible:ring-1 focus-visible:ring-ring font-mono"
|
|
270
|
+
/>
|
|
271
|
+
</td>
|
|
272
|
+
</tr>
|
|
273
|
+
</tbody>
|
|
274
|
+
</table>
|
|
275
|
+
</>
|
|
276
|
+
)}
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
)
|
|
280
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect } from 'react'
|
|
4
|
+
import { FileText, WarningCircle } from '@phosphor-icons/react'
|
|
5
|
+
import {
|
|
6
|
+
Select,
|
|
7
|
+
SelectContent,
|
|
8
|
+
SelectItem,
|
|
9
|
+
SelectTrigger,
|
|
10
|
+
SelectValue,
|
|
11
|
+
} from '@/components/ui/select'
|
|
12
|
+
import type { BrainfishRESTReqBody } from '@/lib/api-docs/types'
|
|
13
|
+
import { KeyValueEditor, KeyValueItem } from './key-value-editor'
|
|
14
|
+
import { CodeEditor, SupportedLanguage } from './code-editor'
|
|
15
|
+
import { cn } from '@/lib/utils'
|
|
16
|
+
|
|
17
|
+
type ContentType = BrainfishRESTReqBody['contentType']
|
|
18
|
+
|
|
19
|
+
const CONTENT_TYPES: { label: string; value: string; language?: SupportedLanguage }[] = [
|
|
20
|
+
{ label: 'None', value: 'none' },
|
|
21
|
+
{ label: 'JSON', value: 'application/json', language: 'json' },
|
|
22
|
+
{ label: 'XML', value: 'application/xml', language: 'xml' },
|
|
23
|
+
{ label: 'Plain Text', value: 'text/plain', language: 'plaintext' },
|
|
24
|
+
{ label: 'HTML', value: 'text/html', language: 'html' },
|
|
25
|
+
{ label: 'Form URL Encoded', value: 'application/x-www-form-urlencoded' },
|
|
26
|
+
{ label: 'Multipart Form', value: 'multipart/form-data' },
|
|
27
|
+
{ label: 'Binary', value: 'application/octet-stream' },
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
interface BodyEditorProps {
|
|
31
|
+
body: string | null
|
|
32
|
+
contentType: ContentType
|
|
33
|
+
onBodyChange: (body: string | null) => void
|
|
34
|
+
onContentTypeChange: (contentType: ContentType) => void
|
|
35
|
+
/** Show error styling to indicate this section needs attention */
|
|
36
|
+
showError?: boolean
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function BodyEditor({
|
|
40
|
+
body,
|
|
41
|
+
contentType,
|
|
42
|
+
onBodyChange,
|
|
43
|
+
onContentTypeChange,
|
|
44
|
+
showError = false,
|
|
45
|
+
}: BodyEditorProps) {
|
|
46
|
+
const [formData, setFormData] = useState<KeyValueItem[]>([])
|
|
47
|
+
|
|
48
|
+
// Parse form data from body when content type changes
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (contentType === 'application/x-www-form-urlencoded' || contentType === 'multipart/form-data') {
|
|
51
|
+
try {
|
|
52
|
+
if (body) {
|
|
53
|
+
const parsed = JSON.parse(body)
|
|
54
|
+
if (Array.isArray(parsed)) {
|
|
55
|
+
setFormData(parsed.map((item: { key: string; value: string }, i: number) => ({
|
|
56
|
+
id: `form_${i}`,
|
|
57
|
+
key: item.key || '',
|
|
58
|
+
value: item.value || '',
|
|
59
|
+
active: true,
|
|
60
|
+
})))
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
setFormData([])
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}, [contentType, body])
|
|
68
|
+
|
|
69
|
+
// Default templates for each content type
|
|
70
|
+
const DEFAULT_TEMPLATES: Record<string, string> = {
|
|
71
|
+
'application/json': '{\n \n}',
|
|
72
|
+
'application/xml': '<?xml version="1.0" encoding="UTF-8"?>\n<root>\n \n</root>',
|
|
73
|
+
'text/html': '<!DOCTYPE html>\n<html>\n<head>\n <title></title>\n</head>\n<body>\n \n</body>\n</html>',
|
|
74
|
+
'text/plain': '',
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Check if current body is a default template or invalid (should be replaced when switching types)
|
|
78
|
+
const isDefaultTemplate = (currentBody: string | null): boolean => {
|
|
79
|
+
if (!currentBody || currentBody.trim() === '' || currentBody.trim() === 'null') return true
|
|
80
|
+
return Object.values(DEFAULT_TEMPLATES).some(
|
|
81
|
+
template => currentBody.trim() === template.trim()
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Normalize body value - convert "null" string to empty
|
|
86
|
+
const normalizedBody = (body === 'null' || body === null) ? '' : body
|
|
87
|
+
|
|
88
|
+
const handleContentTypeChange = (value: string) => {
|
|
89
|
+
console.log('[BodyEditor] Content type changed to:', value)
|
|
90
|
+
const newType = value === 'none' ? null : value as ContentType
|
|
91
|
+
onContentTypeChange(newType)
|
|
92
|
+
|
|
93
|
+
// Set default body content based on type
|
|
94
|
+
if (newType === null) {
|
|
95
|
+
onBodyChange(null)
|
|
96
|
+
} else if (isDefaultTemplate(body)) {
|
|
97
|
+
// Replace with new template if current body is empty or a default template
|
|
98
|
+
const template = DEFAULT_TEMPLATES[newType] ?? ''
|
|
99
|
+
onBodyChange(template)
|
|
100
|
+
}
|
|
101
|
+
// If body has custom content, keep it when switching types
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const handleFormDataChange = (items: KeyValueItem[]) => {
|
|
105
|
+
setFormData(items)
|
|
106
|
+
onBodyChange(JSON.stringify(items.map(({ key, value }) => ({ key, value }))))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const currentValue = contentType || 'none'
|
|
110
|
+
const currentTypeInfo = CONTENT_TYPES.find((t) => t.value === currentValue)
|
|
111
|
+
const editorLanguage = currentTypeInfo?.language || 'plaintext'
|
|
112
|
+
|
|
113
|
+
// Content type selector header component
|
|
114
|
+
const ContentTypeHeader = () => (
|
|
115
|
+
<div className={cn(
|
|
116
|
+
"flex items-center border-b bg-muted/30 px-4 py-2",
|
|
117
|
+
showError ? "border-red-500 bg-red-500/5" : "border-border"
|
|
118
|
+
)}>
|
|
119
|
+
{showError && (
|
|
120
|
+
<WarningCircle className="h-4 w-4 text-red-500 animate-pulse mr-2" weight="fill" />
|
|
121
|
+
)}
|
|
122
|
+
<span className={cn(
|
|
123
|
+
"text-xs font-medium mr-3",
|
|
124
|
+
showError ? "text-red-500" : "text-muted-foreground"
|
|
125
|
+
)}>
|
|
126
|
+
{showError ? "Body needs attention" : "Type"}
|
|
127
|
+
</span>
|
|
128
|
+
<Select value={currentValue} onValueChange={handleContentTypeChange}>
|
|
129
|
+
<SelectTrigger className="h-8 w-auto min-w-[140px] text-sm border-0 bg-transparent hover:bg-muted/50">
|
|
130
|
+
<SelectValue placeholder="Select type" />
|
|
131
|
+
</SelectTrigger>
|
|
132
|
+
<SelectContent>
|
|
133
|
+
{CONTENT_TYPES.map((type) => (
|
|
134
|
+
<SelectItem key={type.value} value={type.value}>
|
|
135
|
+
{type.label}
|
|
136
|
+
</SelectItem>
|
|
137
|
+
))}
|
|
138
|
+
</SelectContent>
|
|
139
|
+
</Select>
|
|
140
|
+
</div>
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
// Form data editors (URL encoded, multipart)
|
|
144
|
+
if (contentType === 'application/x-www-form-urlencoded' || contentType === 'multipart/form-data') {
|
|
145
|
+
return (
|
|
146
|
+
<div className="flex flex-col h-full">
|
|
147
|
+
<ContentTypeHeader />
|
|
148
|
+
<div className="flex-1 overflow-auto">
|
|
149
|
+
<KeyValueEditor
|
|
150
|
+
items={formData}
|
|
151
|
+
onChange={handleFormDataChange}
|
|
152
|
+
title={contentType === 'multipart/form-data' ? 'Form Data' : 'URL Encoded'}
|
|
153
|
+
keyLabel="Field"
|
|
154
|
+
valueLabel="Value"
|
|
155
|
+
showDescription={false}
|
|
156
|
+
/>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Binary file (placeholder)
|
|
163
|
+
if (contentType === 'application/octet-stream') {
|
|
164
|
+
return (
|
|
165
|
+
<div className="flex flex-col h-full">
|
|
166
|
+
<ContentTypeHeader />
|
|
167
|
+
<div className="flex-1 flex flex-col items-center justify-center p-8 text-center">
|
|
168
|
+
<FileText className="h-10 w-10 text-muted-foreground/50 mb-3" weight="duotone" />
|
|
169
|
+
<p className="text-sm text-muted-foreground">Binary file upload not supported</p>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// No body
|
|
176
|
+
if (contentType === null) {
|
|
177
|
+
return (
|
|
178
|
+
<div className="flex flex-col h-full">
|
|
179
|
+
<ContentTypeHeader />
|
|
180
|
+
<div className="flex-1 flex flex-col items-center justify-center p-8 text-center">
|
|
181
|
+
<FileText className="h-10 w-10 text-muted-foreground/50 mb-3" weight="duotone" />
|
|
182
|
+
<p className="text-sm text-muted-foreground">No request body</p>
|
|
183
|
+
<p className="text-xs text-muted-foreground/70 mt-1">Select a type to add body</p>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Monaco editor for JSON, XML, Text, HTML
|
|
190
|
+
const editorValue = normalizedBody || ''
|
|
191
|
+
|
|
192
|
+
// Placeholder text based on content type
|
|
193
|
+
const placeholderMap: Record<string, string> = {
|
|
194
|
+
'application/json': 'Enter JSON body...',
|
|
195
|
+
'application/xml': 'Enter XML body...',
|
|
196
|
+
'text/html': 'Enter HTML body...',
|
|
197
|
+
'text/plain': 'Enter text...',
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const placeholder = placeholderMap[contentType || ''] || 'Enter request body...'
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<div className="flex flex-col h-full">
|
|
204
|
+
<ContentTypeHeader />
|
|
205
|
+
<div className="flex-1 dark-editor h-full">
|
|
206
|
+
<CodeEditor
|
|
207
|
+
key={`editor-${contentType}`}
|
|
208
|
+
value={editorValue}
|
|
209
|
+
onChange={onBodyChange}
|
|
210
|
+
language={editorLanguage}
|
|
211
|
+
height="100%"
|
|
212
|
+
theme="dark"
|
|
213
|
+
showLineNumbers={false}
|
|
214
|
+
showBorder={false}
|
|
215
|
+
rounded={false}
|
|
216
|
+
placeholder={placeholder}
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
)
|
|
221
|
+
}
|