@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,84 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { join } from 'path'
|
|
3
|
+
import { readFileSync, existsSync } from 'fs'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* robots.txt - Auto-generated robots file for search engines
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const DOCS_DIR = join(process.cwd(), 'templates', 'starter')
|
|
10
|
+
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'
|
|
11
|
+
|
|
12
|
+
interface DocsConfig {
|
|
13
|
+
seo?: {
|
|
14
|
+
metatags?: Record<string, string>
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function loadDocsConfig(): DocsConfig | null {
|
|
19
|
+
const configPath = join(DOCS_DIR, 'docs.json')
|
|
20
|
+
if (!existsSync(configPath)) return null
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const content = readFileSync(configPath, 'utf-8')
|
|
24
|
+
return JSON.parse(content)
|
|
25
|
+
} catch {
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function generateRobotsTxt(config: DocsConfig): string {
|
|
31
|
+
const baseUrl = config.seo?.metatags?.canonical || BASE_URL
|
|
32
|
+
const isNoIndex = config.seo?.metatags?.robots === 'noindex'
|
|
33
|
+
|
|
34
|
+
const lines: string[] = []
|
|
35
|
+
|
|
36
|
+
lines.push('# robots.txt generated by Benjy')
|
|
37
|
+
lines.push('')
|
|
38
|
+
lines.push('User-agent: *')
|
|
39
|
+
|
|
40
|
+
if (isNoIndex) {
|
|
41
|
+
lines.push('Disallow: /')
|
|
42
|
+
} else {
|
|
43
|
+
lines.push('Allow: /')
|
|
44
|
+
lines.push('')
|
|
45
|
+
lines.push('# Disallow API routes')
|
|
46
|
+
lines.push('Disallow: /api/')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
lines.push('')
|
|
50
|
+
lines.push(`Sitemap: ${baseUrl}/sitemap.xml`)
|
|
51
|
+
lines.push('')
|
|
52
|
+
|
|
53
|
+
return lines.join('\n')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function GET() {
|
|
57
|
+
try {
|
|
58
|
+
// Check for custom robots.txt first
|
|
59
|
+
const customPath = join(DOCS_DIR, 'robots.txt')
|
|
60
|
+
if (existsSync(customPath)) {
|
|
61
|
+
const customContent = readFileSync(customPath, 'utf-8')
|
|
62
|
+
return new NextResponse(customContent, {
|
|
63
|
+
headers: {
|
|
64
|
+
'Content-Type': 'text/plain; charset=utf-8',
|
|
65
|
+
'Cache-Control': 'public, max-age=3600',
|
|
66
|
+
},
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Generate default robots.txt
|
|
71
|
+
const config = loadDocsConfig()
|
|
72
|
+
const content = generateRobotsTxt(config || {})
|
|
73
|
+
|
|
74
|
+
return new NextResponse(content, {
|
|
75
|
+
headers: {
|
|
76
|
+
'Content-Type': 'text/plain; charset=utf-8',
|
|
77
|
+
'Cache-Control': 'public, max-age=3600',
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error('Error generating robots.txt:', error)
|
|
82
|
+
return new NextResponse('Error generating robots.txt', { status: 500 })
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { join } from 'path'
|
|
3
|
+
import { readFileSync, existsSync, readdirSync, statSync } from 'fs'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* sitemap.xml - Auto-generated sitemap for search engines
|
|
7
|
+
*
|
|
8
|
+
* Generated from docs.json configuration.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const DOCS_DIR = join(process.cwd(), 'templates', 'starter')
|
|
12
|
+
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'
|
|
13
|
+
|
|
14
|
+
interface NavigationTab {
|
|
15
|
+
tab: string
|
|
16
|
+
type: 'docs' | 'openapi' | 'changelog'
|
|
17
|
+
path?: string
|
|
18
|
+
groups?: Array<{
|
|
19
|
+
group: string
|
|
20
|
+
pages: string[]
|
|
21
|
+
}>
|
|
22
|
+
versions?: Array<{
|
|
23
|
+
version: string
|
|
24
|
+
spec: string
|
|
25
|
+
default?: boolean
|
|
26
|
+
}>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface DocsConfig {
|
|
30
|
+
name?: string
|
|
31
|
+
seo?: {
|
|
32
|
+
indexing?: 'all' | 'navigation'
|
|
33
|
+
metatags?: Record<string, string>
|
|
34
|
+
}
|
|
35
|
+
navigation?: {
|
|
36
|
+
tabs?: NavigationTab[]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface SitemapEntry {
|
|
41
|
+
url: string
|
|
42
|
+
lastmod?: string
|
|
43
|
+
changefreq?: string
|
|
44
|
+
priority?: number
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function loadDocsConfig(): DocsConfig | null {
|
|
48
|
+
const configPath = join(DOCS_DIR, 'docs.json')
|
|
49
|
+
if (!existsSync(configPath)) return null
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const content = readFileSync(configPath, 'utf-8')
|
|
53
|
+
return JSON.parse(content)
|
|
54
|
+
} catch {
|
|
55
|
+
return null
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getFileLastModified(slug: string): string | undefined {
|
|
60
|
+
const extensions = ['.mdx', '.md']
|
|
61
|
+
|
|
62
|
+
for (const ext of extensions) {
|
|
63
|
+
const filePath = join(DOCS_DIR, `${slug}${ext}`)
|
|
64
|
+
if (existsSync(filePath)) {
|
|
65
|
+
try {
|
|
66
|
+
const stats = statSync(filePath)
|
|
67
|
+
return stats.mtime.toISOString().split('T')[0]
|
|
68
|
+
} catch {
|
|
69
|
+
return undefined
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return undefined
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getChangelogFiles(): string[] {
|
|
77
|
+
const changelogDir = join(DOCS_DIR, 'changelog')
|
|
78
|
+
if (!existsSync(changelogDir)) return []
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
return readdirSync(changelogDir)
|
|
82
|
+
.filter(f => (f.endsWith('.mdx') || f.endsWith('.md')) && f !== 'latest.mdx')
|
|
83
|
+
.map(f => f.replace(/\.(mdx?|md)$/, ''))
|
|
84
|
+
} catch {
|
|
85
|
+
return []
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function generateSitemap(config: DocsConfig): string {
|
|
90
|
+
const entries: SitemapEntry[] = []
|
|
91
|
+
const baseUrl = config.seo?.metatags?.canonical || BASE_URL
|
|
92
|
+
|
|
93
|
+
// Add homepage
|
|
94
|
+
entries.push({
|
|
95
|
+
url: baseUrl,
|
|
96
|
+
changefreq: 'weekly',
|
|
97
|
+
priority: 1.0,
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// Process navigation tabs
|
|
101
|
+
if (config.navigation?.tabs) {
|
|
102
|
+
for (const tab of config.navigation.tabs) {
|
|
103
|
+
if (tab.type === 'docs' && tab.groups) {
|
|
104
|
+
for (const group of tab.groups) {
|
|
105
|
+
for (const page of group.pages) {
|
|
106
|
+
const lastmod = getFileLastModified(page)
|
|
107
|
+
const urlPath = page === 'index' ? '' : page
|
|
108
|
+
|
|
109
|
+
entries.push({
|
|
110
|
+
url: `${baseUrl}/#${tab.tab.toLowerCase()}/page/${urlPath}`.replace(/\/+$/, ''),
|
|
111
|
+
lastmod,
|
|
112
|
+
changefreq: 'weekly',
|
|
113
|
+
priority: page === 'index' ? 0.9 : 0.8,
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} else if (tab.type === 'openapi') {
|
|
118
|
+
entries.push({
|
|
119
|
+
url: `${baseUrl}/#api-reference`,
|
|
120
|
+
changefreq: 'weekly',
|
|
121
|
+
priority: 0.9,
|
|
122
|
+
})
|
|
123
|
+
} else if (tab.type === 'changelog') {
|
|
124
|
+
entries.push({
|
|
125
|
+
url: `${baseUrl}/#changelog`,
|
|
126
|
+
changefreq: 'weekly',
|
|
127
|
+
priority: 0.7,
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
// Add individual changelog pages
|
|
131
|
+
const versions = getChangelogFiles()
|
|
132
|
+
for (const version of versions) {
|
|
133
|
+
const lastmod = getFileLastModified(`changelog/${version}`)
|
|
134
|
+
entries.push({
|
|
135
|
+
url: `${baseUrl}/#changelog/page/changelog/${version}`,
|
|
136
|
+
lastmod,
|
|
137
|
+
changefreq: 'monthly',
|
|
138
|
+
priority: 0.6,
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Generate XML
|
|
146
|
+
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
147
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
148
|
+
${entries.map(entry => ` <url>
|
|
149
|
+
<loc>${escapeXml(entry.url)}</loc>
|
|
150
|
+
${entry.lastmod ? ` <lastmod>${entry.lastmod}</lastmod>\n` : ''}${entry.changefreq ? ` <changefreq>${entry.changefreq}</changefreq>\n` : ''}${entry.priority !== undefined ? ` <priority>${entry.priority}</priority>\n` : ''} </url>`).join('\n')}
|
|
151
|
+
</urlset>`
|
|
152
|
+
|
|
153
|
+
return xml
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function escapeXml(str: string): string {
|
|
157
|
+
return str
|
|
158
|
+
.replace(/&/g, '&')
|
|
159
|
+
.replace(/</g, '<')
|
|
160
|
+
.replace(/>/g, '>')
|
|
161
|
+
.replace(/"/g, '"')
|
|
162
|
+
.replace(/'/g, ''')
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export async function GET() {
|
|
166
|
+
try {
|
|
167
|
+
// Check for custom sitemap.xml first
|
|
168
|
+
const customPath = join(DOCS_DIR, 'sitemap.xml')
|
|
169
|
+
if (existsSync(customPath)) {
|
|
170
|
+
const customContent = readFileSync(customPath, 'utf-8')
|
|
171
|
+
return new NextResponse(customContent, {
|
|
172
|
+
headers: {
|
|
173
|
+
'Content-Type': 'application/xml; charset=utf-8',
|
|
174
|
+
'Cache-Control': 'public, max-age=3600',
|
|
175
|
+
},
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Generate from docs.json config
|
|
180
|
+
const config = loadDocsConfig()
|
|
181
|
+
if (!config) {
|
|
182
|
+
return new NextResponse('<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"></urlset>', {
|
|
183
|
+
headers: { 'Content-Type': 'application/xml; charset=utf-8' },
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const content = generateSitemap(config)
|
|
188
|
+
|
|
189
|
+
return new NextResponse(content, {
|
|
190
|
+
headers: {
|
|
191
|
+
'Content-Type': 'application/xml; charset=utf-8',
|
|
192
|
+
'Cache-Control': 'public, max-age=3600',
|
|
193
|
+
},
|
|
194
|
+
})
|
|
195
|
+
} catch (error) {
|
|
196
|
+
console.error('Error generating sitemap.xml:', error)
|
|
197
|
+
return new NextResponse('Error generating sitemap.xml', { status: 500 })
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation Components Exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// MDX Components
|
|
6
|
+
export * from './mdx'
|
|
7
|
+
|
|
8
|
+
// MDX Renderer
|
|
9
|
+
export { MDXRenderer, TableOfContents as MDXTableOfContents, type TableOfContentsItem } from './mdx-renderer'
|
|
10
|
+
|
|
11
|
+
// Navigation Components
|
|
12
|
+
export * from './navigation'
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useState, createContext, useContext } from 'react'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
import { CaretDown } from '@phosphor-icons/react'
|
|
6
|
+
import * as PhosphorIcons from '@phosphor-icons/react'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Accordion Components for MDX Documentation
|
|
10
|
+
*
|
|
11
|
+
* Mintlify-compatible accordion components for collapsible content sections.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// Dynamic icon resolver
|
|
15
|
+
function getIcon(iconName: string): React.ComponentType<{ className?: string; weight?: string }> | null {
|
|
16
|
+
if (!iconName) return null
|
|
17
|
+
|
|
18
|
+
const pascalCase = iconName
|
|
19
|
+
.split(/[-_]/)
|
|
20
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
21
|
+
.join('')
|
|
22
|
+
|
|
23
|
+
const Icon = (PhosphorIcons as Record<string, unknown>)[pascalCase] as React.ComponentType<{ className?: string; weight?: string }> | undefined
|
|
24
|
+
return Icon || null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Context for AccordionGroup
|
|
28
|
+
interface AccordionGroupContextType {
|
|
29
|
+
openItems: Set<string>
|
|
30
|
+
toggleItem: (id: string) => void
|
|
31
|
+
allowMultiple: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const AccordionGroupContext = createContext<AccordionGroupContextType | null>(null)
|
|
35
|
+
|
|
36
|
+
interface AccordionProps {
|
|
37
|
+
title: string
|
|
38
|
+
children: React.ReactNode
|
|
39
|
+
description?: string
|
|
40
|
+
icon?: string
|
|
41
|
+
iconType?: string
|
|
42
|
+
defaultOpen?: boolean
|
|
43
|
+
className?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function Accordion({
|
|
47
|
+
title,
|
|
48
|
+
children,
|
|
49
|
+
description,
|
|
50
|
+
icon,
|
|
51
|
+
defaultOpen = false,
|
|
52
|
+
className,
|
|
53
|
+
}: AccordionProps) {
|
|
54
|
+
const groupContext = useContext(AccordionGroupContext)
|
|
55
|
+
const [localOpen, setLocalOpen] = useState(defaultOpen)
|
|
56
|
+
|
|
57
|
+
// Generate a stable ID for this accordion
|
|
58
|
+
const id = React.useId()
|
|
59
|
+
|
|
60
|
+
// Use group context if available, otherwise use local state
|
|
61
|
+
const isOpen = groupContext ? groupContext.openItems.has(id) : localOpen
|
|
62
|
+
const toggle = () => {
|
|
63
|
+
if (groupContext) {
|
|
64
|
+
groupContext.toggleItem(id)
|
|
65
|
+
} else {
|
|
66
|
+
setLocalOpen(!localOpen)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Register default open state with group on mount
|
|
71
|
+
React.useEffect(() => {
|
|
72
|
+
if (groupContext && defaultOpen) {
|
|
73
|
+
groupContext.toggleItem(id)
|
|
74
|
+
}
|
|
75
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
76
|
+
}, [])
|
|
77
|
+
|
|
78
|
+
const Icon = icon ? getIcon(icon) : null
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div
|
|
82
|
+
className={cn(
|
|
83
|
+
'border border-border rounded-lg overflow-hidden',
|
|
84
|
+
'bg-card',
|
|
85
|
+
className
|
|
86
|
+
)}
|
|
87
|
+
>
|
|
88
|
+
<button
|
|
89
|
+
type="button"
|
|
90
|
+
onClick={toggle}
|
|
91
|
+
className={cn(
|
|
92
|
+
'flex w-full items-center gap-3 p-4 text-left',
|
|
93
|
+
'hover:bg-muted/50 transition-colors',
|
|
94
|
+
'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50'
|
|
95
|
+
)}
|
|
96
|
+
aria-expanded={isOpen}
|
|
97
|
+
>
|
|
98
|
+
{Icon && (
|
|
99
|
+
<Icon className="h-5 w-5 shrink-0 text-primary" weight="duotone" />
|
|
100
|
+
)}
|
|
101
|
+
|
|
102
|
+
<div className="flex-1 min-w-0">
|
|
103
|
+
<div className="font-medium text-foreground">{title}</div>
|
|
104
|
+
{description && (
|
|
105
|
+
<div className="text-sm text-muted-foreground mt-0.5">{description}</div>
|
|
106
|
+
)}
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<CaretDown
|
|
110
|
+
className={cn(
|
|
111
|
+
'h-5 w-5 shrink-0 text-muted-foreground transition-transform duration-200',
|
|
112
|
+
isOpen && 'rotate-180'
|
|
113
|
+
)}
|
|
114
|
+
weight="bold"
|
|
115
|
+
/>
|
|
116
|
+
</button>
|
|
117
|
+
|
|
118
|
+
<div
|
|
119
|
+
className={cn(
|
|
120
|
+
'grid transition-all duration-200 ease-in-out',
|
|
121
|
+
isOpen ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'
|
|
122
|
+
)}
|
|
123
|
+
>
|
|
124
|
+
<div className="overflow-hidden">
|
|
125
|
+
<div className="px-4 pb-4 text-sm text-foreground/90 [&>p]:mb-2 [&>p:last-child]:mb-0">
|
|
126
|
+
{children}
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
interface AccordionGroupProps {
|
|
135
|
+
children: React.ReactNode
|
|
136
|
+
allowMultiple?: boolean
|
|
137
|
+
className?: string
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function AccordionGroup({
|
|
141
|
+
children,
|
|
142
|
+
allowMultiple = true,
|
|
143
|
+
className,
|
|
144
|
+
}: AccordionGroupProps) {
|
|
145
|
+
const [openItems, setOpenItems] = useState<Set<string>>(new Set())
|
|
146
|
+
|
|
147
|
+
const toggleItem = (id: string) => {
|
|
148
|
+
setOpenItems(prev => {
|
|
149
|
+
const next = new Set(prev)
|
|
150
|
+
if (next.has(id)) {
|
|
151
|
+
next.delete(id)
|
|
152
|
+
} else {
|
|
153
|
+
if (!allowMultiple) {
|
|
154
|
+
next.clear()
|
|
155
|
+
}
|
|
156
|
+
next.add(id)
|
|
157
|
+
}
|
|
158
|
+
return next
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<AccordionGroupContext.Provider value={{ openItems, toggleItem, allowMultiple }}>
|
|
164
|
+
<div className={cn('space-y-2 my-4', className)}>
|
|
165
|
+
{children}
|
|
166
|
+
</div>
|
|
167
|
+
</AccordionGroupContext.Provider>
|
|
168
|
+
)
|
|
169
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { ReactNode } from 'react'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
import * as PhosphorIcons from '@phosphor-icons/react'
|
|
6
|
+
|
|
7
|
+
// Helper to get Phosphor icon component from name
|
|
8
|
+
function getIcon(iconName?: string): React.ComponentType<{ className?: string; weight?: "thin" | "light" | "regular" | "bold" | "fill" | "duotone" }> | null {
|
|
9
|
+
if (!iconName) return null
|
|
10
|
+
|
|
11
|
+
// Convert kebab-case to PascalCase for Phosphor icons
|
|
12
|
+
const pascalCase = iconName
|
|
13
|
+
.split('-')
|
|
14
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
15
|
+
.join('')
|
|
16
|
+
|
|
17
|
+
const IconComponent = (PhosphorIcons as Record<string, unknown>)[pascalCase] as React.ComponentType<{ className?: string; weight?: "thin" | "light" | "regular" | "bold" | "fill" | "duotone" }> | undefined
|
|
18
|
+
return IconComponent || null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type BadgeColor =
|
|
22
|
+
| 'gray'
|
|
23
|
+
| 'blue'
|
|
24
|
+
| 'green'
|
|
25
|
+
| 'yellow'
|
|
26
|
+
| 'orange'
|
|
27
|
+
| 'red'
|
|
28
|
+
| 'purple'
|
|
29
|
+
| 'white'
|
|
30
|
+
| 'surface'
|
|
31
|
+
| 'white-destructive'
|
|
32
|
+
| 'surface-destructive'
|
|
33
|
+
|
|
34
|
+
type BadgeSize = 'xs' | 'sm' | 'md' | 'lg'
|
|
35
|
+
type BadgeShape = 'rounded' | 'pill'
|
|
36
|
+
|
|
37
|
+
interface BadgeProps {
|
|
38
|
+
children: ReactNode
|
|
39
|
+
color?: BadgeColor
|
|
40
|
+
size?: BadgeSize
|
|
41
|
+
shape?: BadgeShape
|
|
42
|
+
icon?: string
|
|
43
|
+
stroke?: boolean
|
|
44
|
+
disabled?: boolean
|
|
45
|
+
className?: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Color variants - filled background
|
|
49
|
+
const colorVariants: Record<BadgeColor, string> = {
|
|
50
|
+
gray: 'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300',
|
|
51
|
+
blue: 'bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300',
|
|
52
|
+
green: 'bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300',
|
|
53
|
+
yellow: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-300',
|
|
54
|
+
orange: 'bg-orange-100 text-orange-700 dark:bg-orange-900/50 dark:text-orange-300',
|
|
55
|
+
red: 'bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300',
|
|
56
|
+
purple: 'bg-purple-100 text-purple-700 dark:bg-purple-900/50 dark:text-purple-300',
|
|
57
|
+
white: 'bg-white text-gray-900 border border-gray-200 dark:bg-gray-900 dark:text-gray-100 dark:border-gray-700',
|
|
58
|
+
surface: 'bg-muted text-foreground',
|
|
59
|
+
'white-destructive': 'bg-white text-red-600 border border-red-200 dark:bg-gray-900 dark:text-red-400 dark:border-red-800',
|
|
60
|
+
'surface-destructive': 'bg-red-50 text-red-600 dark:bg-red-950 dark:text-red-400',
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Color variants - stroke/outline
|
|
64
|
+
const strokeVariants: Record<BadgeColor, string> = {
|
|
65
|
+
gray: 'border border-gray-300 text-gray-700 dark:border-gray-600 dark:text-gray-300',
|
|
66
|
+
blue: 'border border-blue-300 text-blue-700 dark:border-blue-600 dark:text-blue-300',
|
|
67
|
+
green: 'border border-green-300 text-green-700 dark:border-green-600 dark:text-green-300',
|
|
68
|
+
yellow: 'border border-yellow-300 text-yellow-700 dark:border-yellow-600 dark:text-yellow-300',
|
|
69
|
+
orange: 'border border-orange-300 text-orange-700 dark:border-orange-600 dark:text-orange-300',
|
|
70
|
+
red: 'border border-red-300 text-red-700 dark:border-red-600 dark:text-red-300',
|
|
71
|
+
purple: 'border border-purple-300 text-purple-700 dark:border-purple-600 dark:text-purple-300',
|
|
72
|
+
white: 'border border-gray-300 text-gray-700 dark:border-gray-600 dark:text-gray-300',
|
|
73
|
+
surface: 'border border-border text-foreground',
|
|
74
|
+
'white-destructive': 'border border-red-300 text-red-600 dark:border-red-600 dark:text-red-400',
|
|
75
|
+
'surface-destructive': 'border border-red-300 text-red-600 dark:border-red-600 dark:text-red-400',
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Size variants
|
|
79
|
+
const sizeVariants: Record<BadgeSize, string> = {
|
|
80
|
+
xs: 'text-[10px] px-1.5 py-0.5 gap-1',
|
|
81
|
+
sm: 'text-xs px-2 py-0.5 gap-1',
|
|
82
|
+
md: 'text-sm px-2.5 py-1 gap-1.5',
|
|
83
|
+
lg: 'text-base px-3 py-1.5 gap-2',
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Icon size variants
|
|
87
|
+
const iconSizeVariants: Record<BadgeSize, string> = {
|
|
88
|
+
xs: 'h-2.5 w-2.5',
|
|
89
|
+
sm: 'h-3 w-3',
|
|
90
|
+
md: 'h-3.5 w-3.5',
|
|
91
|
+
lg: 'h-4 w-4',
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Shape variants
|
|
95
|
+
const shapeVariants: Record<BadgeShape, string> = {
|
|
96
|
+
rounded: 'rounded-md',
|
|
97
|
+
pill: 'rounded-full',
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function Badge({
|
|
101
|
+
children,
|
|
102
|
+
color = 'gray',
|
|
103
|
+
size = 'md',
|
|
104
|
+
shape = 'rounded',
|
|
105
|
+
icon,
|
|
106
|
+
stroke = false,
|
|
107
|
+
disabled = false,
|
|
108
|
+
className,
|
|
109
|
+
}: BadgeProps) {
|
|
110
|
+
const IconComponent = getIcon(icon)
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<span
|
|
114
|
+
className={cn(
|
|
115
|
+
'inline-flex items-center font-medium whitespace-nowrap',
|
|
116
|
+
sizeVariants[size],
|
|
117
|
+
shapeVariants[shape],
|
|
118
|
+
stroke ? strokeVariants[color] : colorVariants[color],
|
|
119
|
+
disabled && 'opacity-50 cursor-not-allowed',
|
|
120
|
+
className
|
|
121
|
+
)}
|
|
122
|
+
>
|
|
123
|
+
{IconComponent && (
|
|
124
|
+
<IconComponent
|
|
125
|
+
className={cn(iconSizeVariants[size], 'shrink-0')}
|
|
126
|
+
weight="bold"
|
|
127
|
+
/>
|
|
128
|
+
)}
|
|
129
|
+
{children}
|
|
130
|
+
</span>
|
|
131
|
+
)
|
|
132
|
+
}
|