@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,240 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useState, useCallback } from 'react'
|
|
4
|
+
import { createPortal } from 'react-dom'
|
|
5
|
+
import { cn } from '@/lib/utils'
|
|
6
|
+
import { ArrowsOut, X } from '@phosphor-icons/react'
|
|
7
|
+
|
|
8
|
+
interface MermaidProps {
|
|
9
|
+
children: string
|
|
10
|
+
className?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Helper to detect dark mode
|
|
14
|
+
function useIsDarkMode() {
|
|
15
|
+
const [isDark, setIsDark] = useState(false)
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const checkDarkMode = () => {
|
|
19
|
+
setIsDark(document.documentElement.classList.contains('dark'))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
checkDarkMode()
|
|
23
|
+
|
|
24
|
+
// Watch for theme changes
|
|
25
|
+
const observer = new MutationObserver(checkDarkMode)
|
|
26
|
+
observer.observe(document.documentElement, {
|
|
27
|
+
attributes: true,
|
|
28
|
+
attributeFilter: ['class']
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
return () => observer.disconnect()
|
|
32
|
+
}, [])
|
|
33
|
+
|
|
34
|
+
return isDark
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function Mermaid({ children, className }: MermaidProps) {
|
|
38
|
+
const contentRef = useRef<HTMLDivElement>(null)
|
|
39
|
+
const [isLoaded, setIsLoaded] = useState(false)
|
|
40
|
+
const [error, setError] = useState<string | null>(null)
|
|
41
|
+
const [isModalOpen, setIsModalOpen] = useState(false)
|
|
42
|
+
const [mounted, setMounted] = useState(false)
|
|
43
|
+
const [modalSvg, setModalSvg] = useState<string>('')
|
|
44
|
+
const [modalLoading, setModalLoading] = useState(false)
|
|
45
|
+
const isDarkMode = useIsDarkMode()
|
|
46
|
+
|
|
47
|
+
const code = typeof children === 'string' ? children.trim() : ''
|
|
48
|
+
|
|
49
|
+
// Track if component is mounted (for portal)
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
setMounted(true)
|
|
52
|
+
}, [])
|
|
53
|
+
|
|
54
|
+
// Render inline diagram
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (!code || !contentRef.current) return
|
|
57
|
+
|
|
58
|
+
const renderDiagram = async () => {
|
|
59
|
+
try {
|
|
60
|
+
const mermaid = (await import('mermaid')).default
|
|
61
|
+
|
|
62
|
+
mermaid.initialize({
|
|
63
|
+
startOnLoad: false,
|
|
64
|
+
theme: isDarkMode ? 'dark' : 'default',
|
|
65
|
+
securityLevel: 'loose',
|
|
66
|
+
fontFamily: 'inherit',
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const id = `mermaid-inline-${Math.random().toString(36).substr(2, 9)}`
|
|
70
|
+
const { svg } = await mermaid.render(id, code)
|
|
71
|
+
|
|
72
|
+
if (contentRef.current) {
|
|
73
|
+
contentRef.current.innerHTML = svg
|
|
74
|
+
setIsLoaded(true)
|
|
75
|
+
setError(null)
|
|
76
|
+
}
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.error('Mermaid rendering error:', err)
|
|
79
|
+
setError(err instanceof Error ? err.message : 'Failed to render diagram')
|
|
80
|
+
setIsLoaded(true)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
renderDiagram()
|
|
85
|
+
}, [code, isDarkMode])
|
|
86
|
+
|
|
87
|
+
// Render modal diagram when modal opens
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (!isModalOpen || !code) return
|
|
90
|
+
|
|
91
|
+
setModalLoading(true)
|
|
92
|
+
setModalSvg('')
|
|
93
|
+
|
|
94
|
+
const renderModalDiagram = async () => {
|
|
95
|
+
try {
|
|
96
|
+
const mermaid = (await import('mermaid')).default
|
|
97
|
+
|
|
98
|
+
mermaid.initialize({
|
|
99
|
+
startOnLoad: false,
|
|
100
|
+
theme: isDarkMode ? 'dark' : 'default',
|
|
101
|
+
securityLevel: 'loose',
|
|
102
|
+
fontFamily: 'inherit',
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const id = `mermaid-modal-${Math.random().toString(36).substr(2, 9)}`
|
|
106
|
+
const { svg } = await mermaid.render(id, code)
|
|
107
|
+
|
|
108
|
+
setModalSvg(svg)
|
|
109
|
+
setModalLoading(false)
|
|
110
|
+
} catch (err) {
|
|
111
|
+
console.error('Mermaid modal rendering error:', err)
|
|
112
|
+
setModalSvg(`<div class="p-4 text-red-500">Failed to render diagram</div>`)
|
|
113
|
+
setModalLoading(false)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
renderModalDiagram()
|
|
118
|
+
}, [isModalOpen, code, isDarkMode])
|
|
119
|
+
|
|
120
|
+
// Close modal on escape key
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
const handleEscape = (e: KeyboardEvent) => {
|
|
123
|
+
if (e.key === 'Escape' && isModalOpen) {
|
|
124
|
+
setIsModalOpen(false)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
document.addEventListener('keydown', handleEscape)
|
|
129
|
+
return () => document.removeEventListener('keydown', handleEscape)
|
|
130
|
+
}, [isModalOpen])
|
|
131
|
+
|
|
132
|
+
// Prevent body scroll when modal is open
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
if (isModalOpen) {
|
|
135
|
+
document.body.style.overflow = 'hidden'
|
|
136
|
+
} else {
|
|
137
|
+
document.body.style.overflow = ''
|
|
138
|
+
}
|
|
139
|
+
return () => {
|
|
140
|
+
document.body.style.overflow = ''
|
|
141
|
+
}
|
|
142
|
+
}, [isModalOpen])
|
|
143
|
+
|
|
144
|
+
const openModal = useCallback(() => {
|
|
145
|
+
setIsModalOpen(true)
|
|
146
|
+
}, [])
|
|
147
|
+
|
|
148
|
+
const closeModal = useCallback(() => {
|
|
149
|
+
setIsModalOpen(false)
|
|
150
|
+
}, [])
|
|
151
|
+
|
|
152
|
+
if (error) {
|
|
153
|
+
return (
|
|
154
|
+
<div className={cn(
|
|
155
|
+
'my-4 p-4 rounded-lg border border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950',
|
|
156
|
+
className
|
|
157
|
+
)}>
|
|
158
|
+
<p className="text-sm text-red-600 dark:text-red-400 font-medium">
|
|
159
|
+
Failed to render Mermaid diagram
|
|
160
|
+
</p>
|
|
161
|
+
<pre className="mt-2 text-xs text-red-500 dark:text-red-400 overflow-auto">
|
|
162
|
+
{error}
|
|
163
|
+
</pre>
|
|
164
|
+
</div>
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<>
|
|
170
|
+
<div
|
|
171
|
+
className={cn(
|
|
172
|
+
'my-4 relative rounded-lg border border-border bg-muted/30 overflow-hidden group',
|
|
173
|
+
className
|
|
174
|
+
)}
|
|
175
|
+
>
|
|
176
|
+
{/* Inline diagram */}
|
|
177
|
+
<div className="overflow-hidden min-h-[200px] relative">
|
|
178
|
+
<div
|
|
179
|
+
ref={contentRef}
|
|
180
|
+
className={cn(
|
|
181
|
+
'flex items-center justify-center p-4',
|
|
182
|
+
!isLoaded && 'opacity-0'
|
|
183
|
+
)}
|
|
184
|
+
/>
|
|
185
|
+
{!isLoaded && (
|
|
186
|
+
<div className="absolute inset-0 flex items-center justify-center">
|
|
187
|
+
<div className="text-sm text-muted-foreground">Loading diagram...</div>
|
|
188
|
+
</div>
|
|
189
|
+
)}
|
|
190
|
+
</div>
|
|
191
|
+
|
|
192
|
+
{/* Fullscreen button */}
|
|
193
|
+
{isLoaded && (
|
|
194
|
+
<button
|
|
195
|
+
onClick={openModal}
|
|
196
|
+
className="absolute bottom-3 right-3 p-2 bg-background/90 backdrop-blur-sm rounded-lg border border-border shadow-sm hover:bg-muted transition-colors opacity-0 group-hover:opacity-100"
|
|
197
|
+
title="View fullscreen"
|
|
198
|
+
>
|
|
199
|
+
<ArrowsOut className="h-4 w-4" weight="bold" />
|
|
200
|
+
</button>
|
|
201
|
+
)}
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
{/* Modal via Portal */}
|
|
205
|
+
{mounted && isModalOpen && createPortal(
|
|
206
|
+
<div
|
|
207
|
+
className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/80 backdrop-blur-sm"
|
|
208
|
+
onClick={closeModal}
|
|
209
|
+
>
|
|
210
|
+
<div
|
|
211
|
+
className="relative bg-background rounded-lg border border-border shadow-2xl w-[95vw] h-[95vh] overflow-auto flex items-center justify-center"
|
|
212
|
+
onClick={(e) => e.stopPropagation()}
|
|
213
|
+
>
|
|
214
|
+
{/* Close button */}
|
|
215
|
+
<button
|
|
216
|
+
onClick={closeModal}
|
|
217
|
+
className="absolute top-4 right-4 p-2 bg-muted/80 hover:bg-muted rounded-lg transition-colors z-10"
|
|
218
|
+
title="Close (Esc)"
|
|
219
|
+
>
|
|
220
|
+
<X className="h-5 w-5" weight="bold" />
|
|
221
|
+
</button>
|
|
222
|
+
|
|
223
|
+
{/* Modal diagram */}
|
|
224
|
+
{modalLoading ? (
|
|
225
|
+
<div className="text-sm text-muted-foreground">Loading diagram...</div>
|
|
226
|
+
) : modalSvg ? (
|
|
227
|
+
<div
|
|
228
|
+
className="p-8 flex items-center justify-center [&_svg]:w-auto [&_svg]:h-auto [&_svg]:min-w-[60vw] [&_svg]:min-h-[60vh] [&_svg]:max-w-[90vw] [&_svg]:max-h-[90vh]"
|
|
229
|
+
dangerouslySetInnerHTML={{ __html: modalSvg }}
|
|
230
|
+
/>
|
|
231
|
+
) : (
|
|
232
|
+
<div className="text-sm text-muted-foreground">No diagram content (modalSvg is empty)</div>
|
|
233
|
+
)}
|
|
234
|
+
</div>
|
|
235
|
+
</div>,
|
|
236
|
+
document.body
|
|
237
|
+
)}
|
|
238
|
+
</>
|
|
239
|
+
)
|
|
240
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* ParamField and ResponseField Components for MDX Documentation
|
|
8
|
+
*
|
|
9
|
+
* Mintlify-compatible components for documenting API parameters and responses.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
interface ParamFieldProps {
|
|
13
|
+
/** Parameter path/name (e.g., "body.user.name" or "query.limit") */
|
|
14
|
+
path?: string
|
|
15
|
+
/** Parameter name (alternative to path) */
|
|
16
|
+
name?: string
|
|
17
|
+
/** Parameter type (e.g., "string", "number", "object") */
|
|
18
|
+
type?: string
|
|
19
|
+
/** Whether the parameter is required */
|
|
20
|
+
required?: boolean
|
|
21
|
+
/** Whether the parameter is deprecated */
|
|
22
|
+
deprecated?: boolean
|
|
23
|
+
/** Default value */
|
|
24
|
+
default?: string
|
|
25
|
+
/** Placeholder text */
|
|
26
|
+
placeholder?: string
|
|
27
|
+
/** Additional description as children */
|
|
28
|
+
children?: React.ReactNode
|
|
29
|
+
className?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function ParamField({
|
|
33
|
+
path,
|
|
34
|
+
name,
|
|
35
|
+
type,
|
|
36
|
+
required = false,
|
|
37
|
+
deprecated = false,
|
|
38
|
+
default: defaultValue,
|
|
39
|
+
placeholder,
|
|
40
|
+
children,
|
|
41
|
+
className,
|
|
42
|
+
}: ParamFieldProps) {
|
|
43
|
+
const paramName = name || path?.split('.').pop() || 'param'
|
|
44
|
+
const paramPath = path || name
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div
|
|
48
|
+
className={cn(
|
|
49
|
+
'py-4 border-b border-border last:border-0',
|
|
50
|
+
deprecated && 'opacity-60',
|
|
51
|
+
className
|
|
52
|
+
)}
|
|
53
|
+
>
|
|
54
|
+
<div className="flex flex-wrap items-baseline gap-2 mb-1">
|
|
55
|
+
{/* Parameter name */}
|
|
56
|
+
<code className="text-sm font-semibold text-foreground">
|
|
57
|
+
{paramName}
|
|
58
|
+
</code>
|
|
59
|
+
|
|
60
|
+
{/* Type badge */}
|
|
61
|
+
{type && (
|
|
62
|
+
<span className="text-xs px-1.5 py-0.5 rounded bg-muted text-muted-foreground font-mono">
|
|
63
|
+
{type}
|
|
64
|
+
</span>
|
|
65
|
+
)}
|
|
66
|
+
|
|
67
|
+
{/* Required badge */}
|
|
68
|
+
{required && (
|
|
69
|
+
<span className="text-xs px-1.5 py-0.5 rounded bg-red-500/10 text-red-600 dark:text-red-400 font-medium">
|
|
70
|
+
required
|
|
71
|
+
</span>
|
|
72
|
+
)}
|
|
73
|
+
|
|
74
|
+
{/* Deprecated badge */}
|
|
75
|
+
{deprecated && (
|
|
76
|
+
<span className="text-xs px-1.5 py-0.5 rounded bg-amber-500/10 text-amber-600 dark:text-amber-400 font-medium">
|
|
77
|
+
deprecated
|
|
78
|
+
</span>
|
|
79
|
+
)}
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
{/* Full path if different from name */}
|
|
83
|
+
{paramPath && paramPath !== paramName && (
|
|
84
|
+
<div className="text-xs text-muted-foreground mb-2 font-mono">
|
|
85
|
+
{paramPath}
|
|
86
|
+
</div>
|
|
87
|
+
)}
|
|
88
|
+
|
|
89
|
+
{/* Default value */}
|
|
90
|
+
{defaultValue && (
|
|
91
|
+
<div className="text-xs text-muted-foreground mb-2">
|
|
92
|
+
Default: <code className="bg-muted px-1 py-0.5 rounded">{defaultValue}</code>
|
|
93
|
+
</div>
|
|
94
|
+
)}
|
|
95
|
+
|
|
96
|
+
{/* Placeholder */}
|
|
97
|
+
{placeholder && (
|
|
98
|
+
<div className="text-xs text-muted-foreground mb-2">
|
|
99
|
+
Example: <code className="bg-muted px-1 py-0.5 rounded">{placeholder}</code>
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
102
|
+
|
|
103
|
+
{/* Description */}
|
|
104
|
+
{children && (
|
|
105
|
+
<div className="text-sm text-foreground/90 [&>p]:mb-2 [&>p:last-child]:mb-0">
|
|
106
|
+
{children}
|
|
107
|
+
</div>
|
|
108
|
+
)}
|
|
109
|
+
</div>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
interface ResponseFieldProps {
|
|
114
|
+
/** Field name */
|
|
115
|
+
name: string
|
|
116
|
+
/** Field type */
|
|
117
|
+
type?: string
|
|
118
|
+
/** Whether the field is always present */
|
|
119
|
+
required?: boolean
|
|
120
|
+
/** Additional description */
|
|
121
|
+
children?: React.ReactNode
|
|
122
|
+
className?: string
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function ResponseField({
|
|
126
|
+
name,
|
|
127
|
+
type,
|
|
128
|
+
required = false,
|
|
129
|
+
children,
|
|
130
|
+
className,
|
|
131
|
+
}: ResponseFieldProps) {
|
|
132
|
+
return (
|
|
133
|
+
<div className={cn('py-4 border-b border-border last:border-0', className)}>
|
|
134
|
+
<div className="flex flex-wrap items-baseline gap-2 mb-1">
|
|
135
|
+
{/* Field name */}
|
|
136
|
+
<code className="text-sm font-semibold text-foreground">
|
|
137
|
+
{name}
|
|
138
|
+
</code>
|
|
139
|
+
|
|
140
|
+
{/* Type badge */}
|
|
141
|
+
{type && (
|
|
142
|
+
<span className="text-xs px-1.5 py-0.5 rounded bg-muted text-muted-foreground font-mono">
|
|
143
|
+
{type}
|
|
144
|
+
</span>
|
|
145
|
+
)}
|
|
146
|
+
|
|
147
|
+
{/* Required/always present badge */}
|
|
148
|
+
{required && (
|
|
149
|
+
<span className="text-xs px-1.5 py-0.5 rounded bg-green-500/10 text-green-600 dark:text-green-400 font-medium">
|
|
150
|
+
always present
|
|
151
|
+
</span>
|
|
152
|
+
)}
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
{/* Description */}
|
|
156
|
+
{children && (
|
|
157
|
+
<div className="text-sm text-foreground/90 [&>p]:mb-2 [&>p:last-child]:mb-0">
|
|
158
|
+
{children}
|
|
159
|
+
</div>
|
|
160
|
+
)}
|
|
161
|
+
</div>
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Expandable component for nested objects
|
|
167
|
+
*/
|
|
168
|
+
interface ExpandableProps {
|
|
169
|
+
title?: string
|
|
170
|
+
defaultOpen?: boolean
|
|
171
|
+
children: React.ReactNode
|
|
172
|
+
className?: string
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function Expandable({
|
|
176
|
+
title = 'Show child attributes',
|
|
177
|
+
defaultOpen = false,
|
|
178
|
+
children,
|
|
179
|
+
className,
|
|
180
|
+
}: ExpandableProps) {
|
|
181
|
+
const [isOpen, setIsOpen] = React.useState(defaultOpen)
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<div className={cn('mt-2 ml-4 border-l-2 border-border', className)}>
|
|
185
|
+
<button
|
|
186
|
+
type="button"
|
|
187
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
188
|
+
className="text-xs text-primary hover:text-primary/80 transition-colors pl-3 py-1"
|
|
189
|
+
>
|
|
190
|
+
{isOpen ? 'Hide' : 'Show'} {title.replace('Show ', '').replace('Hide ', '')}
|
|
191
|
+
</button>
|
|
192
|
+
|
|
193
|
+
{isOpen && (
|
|
194
|
+
<div className="pl-4 pt-2">
|
|
195
|
+
{children}
|
|
196
|
+
</div>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
)
|
|
200
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
import * as PhosphorIcons from '@phosphor-icons/react'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Steps Components for MDX Documentation
|
|
9
|
+
*
|
|
10
|
+
* Mintlify-compatible step-by-step guide components.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Dynamic icon resolver
|
|
14
|
+
function getIcon(iconName: string): React.ComponentType<{ className?: string; weight?: string }> | null {
|
|
15
|
+
if (!iconName) return null
|
|
16
|
+
|
|
17
|
+
const pascalCase = iconName
|
|
18
|
+
.split(/[-_]/)
|
|
19
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
20
|
+
.join('')
|
|
21
|
+
|
|
22
|
+
const Icon = (PhosphorIcons as Record<string, unknown>)[pascalCase] as React.ComponentType<{ className?: string; weight?: string }> | undefined
|
|
23
|
+
return Icon || null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface StepProps {
|
|
27
|
+
title: string
|
|
28
|
+
children: React.ReactNode
|
|
29
|
+
icon?: string
|
|
30
|
+
iconType?: string
|
|
31
|
+
stepNumber?: number
|
|
32
|
+
titleSize?: 'p' | 'h2' | 'h3' | 'h4'
|
|
33
|
+
className?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface StepsContextValue {
|
|
37
|
+
titleSize: 'p' | 'h2' | 'h3' | 'h4'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const StepsContext = React.createContext<StepsContextValue>({ titleSize: 'p' })
|
|
41
|
+
|
|
42
|
+
export function Step({
|
|
43
|
+
title,
|
|
44
|
+
children,
|
|
45
|
+
icon,
|
|
46
|
+
stepNumber,
|
|
47
|
+
titleSize: localTitleSize,
|
|
48
|
+
className,
|
|
49
|
+
}: StepProps) {
|
|
50
|
+
const { titleSize: contextTitleSize } = React.useContext(StepsContext)
|
|
51
|
+
const titleSize = localTitleSize ?? contextTitleSize
|
|
52
|
+
|
|
53
|
+
const Icon = icon ? getIcon(icon) : null
|
|
54
|
+
|
|
55
|
+
const TitleTag = titleSize === 'p' ? 'div' : titleSize
|
|
56
|
+
const titleClass = cn(
|
|
57
|
+
'font-semibold text-foreground',
|
|
58
|
+
titleSize === 'h2' && 'text-xl',
|
|
59
|
+
titleSize === 'h3' && 'text-lg',
|
|
60
|
+
titleSize === 'h4' && 'text-base',
|
|
61
|
+
titleSize === 'p' && 'text-base'
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div className={cn('relative pl-8 pb-8 last:pb-0', className)}>
|
|
66
|
+
{/* Vertical line */}
|
|
67
|
+
<div className="absolute left-[11px] top-8 bottom-0 w-px bg-border last:hidden" />
|
|
68
|
+
|
|
69
|
+
{/* Step indicator */}
|
|
70
|
+
<div className="absolute left-0 top-0 flex h-6 w-6 items-center justify-center rounded-full bg-primary text-primary-foreground text-xs font-semibold">
|
|
71
|
+
{Icon ? (
|
|
72
|
+
<Icon className="h-3.5 w-3.5" weight="bold" />
|
|
73
|
+
) : (
|
|
74
|
+
stepNumber
|
|
75
|
+
)}
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
{/* Content */}
|
|
79
|
+
<div className="pt-0.5">
|
|
80
|
+
<TitleTag className={titleClass}>{title}</TitleTag>
|
|
81
|
+
<div className="mt-2 text-sm text-foreground/90 [&>p]:mb-2 [&>p:last-child]:mb-0 [&>pre]:my-3 [&>ul]:mt-2 [&>ol]:mt-2">
|
|
82
|
+
{children}
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface StepsProps {
|
|
90
|
+
children: React.ReactNode
|
|
91
|
+
titleSize?: 'p' | 'h2' | 'h3' | 'h4'
|
|
92
|
+
className?: string
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function Steps({ children, titleSize = 'p', className }: StepsProps) {
|
|
96
|
+
// Auto-number steps
|
|
97
|
+
const numberedChildren = React.Children.map(children, (child, index) => {
|
|
98
|
+
if (React.isValidElement<StepProps>(child) && child.type === Step) {
|
|
99
|
+
return React.cloneElement(child, {
|
|
100
|
+
stepNumber: child.props.stepNumber ?? index + 1,
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
return child
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<StepsContext.Provider value={{ titleSize }}>
|
|
108
|
+
<div className={cn('my-6', className)}>
|
|
109
|
+
{numberedChildren}
|
|
110
|
+
</div>
|
|
111
|
+
</StepsContext.Provider>
|
|
112
|
+
)
|
|
113
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Tabs Components for MDX Documentation
|
|
8
|
+
*
|
|
9
|
+
* Mintlify-compatible tab components for organizing content.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
interface TabProps {
|
|
13
|
+
title: string
|
|
14
|
+
children: React.ReactNode
|
|
15
|
+
className?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface TabsProps {
|
|
19
|
+
children: React.ReactNode
|
|
20
|
+
defaultValue?: string
|
|
21
|
+
className?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Context for sharing tab state
|
|
25
|
+
interface TabsContextValue {
|
|
26
|
+
activeTab: string
|
|
27
|
+
setActiveTab: (tab: string) => void
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const TabsContext = React.createContext<TabsContextValue | null>(null)
|
|
31
|
+
|
|
32
|
+
export function Tab({ children, className }: TabProps) {
|
|
33
|
+
// Tab content is rendered by Tabs parent
|
|
34
|
+
return <div className={className}>{children}</div>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function Tabs({ children, defaultValue, className }: TabsProps) {
|
|
38
|
+
// Extract tab titles and content from children
|
|
39
|
+
const tabs: { title: string; content: React.ReactNode }[] = []
|
|
40
|
+
|
|
41
|
+
React.Children.forEach(children, (child) => {
|
|
42
|
+
if (React.isValidElement<TabProps>(child) && child.type === Tab) {
|
|
43
|
+
tabs.push({
|
|
44
|
+
title: child.props.title,
|
|
45
|
+
content: child.props.children,
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const [activeTab, setActiveTab] = useState(defaultValue || tabs[0]?.title || '')
|
|
51
|
+
|
|
52
|
+
const activeContent = tabs.find(tab => tab.title === activeTab)?.content
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
|
|
56
|
+
<div className={cn('my-4', className)}>
|
|
57
|
+
{/* Tab headers */}
|
|
58
|
+
<div className="flex border-b border-border overflow-x-auto">
|
|
59
|
+
{tabs.map((tab) => (
|
|
60
|
+
<button
|
|
61
|
+
key={tab.title}
|
|
62
|
+
type="button"
|
|
63
|
+
onClick={() => setActiveTab(tab.title)}
|
|
64
|
+
className={cn(
|
|
65
|
+
'px-4 py-2 text-sm font-medium whitespace-nowrap transition-colors',
|
|
66
|
+
'hover:text-foreground',
|
|
67
|
+
'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50',
|
|
68
|
+
'border-b-2 -mb-px',
|
|
69
|
+
activeTab === tab.title
|
|
70
|
+
? 'border-primary text-foreground'
|
|
71
|
+
: 'border-transparent text-muted-foreground'
|
|
72
|
+
)}
|
|
73
|
+
>
|
|
74
|
+
{tab.title}
|
|
75
|
+
</button>
|
|
76
|
+
))}
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
{/* Tab content */}
|
|
80
|
+
<div className="pt-4">
|
|
81
|
+
{activeContent}
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</TabsContext.Provider>
|
|
85
|
+
)
|
|
86
|
+
}
|