create-specra 0.1.0
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.MD +21 -0
- package/README.md +137 -0
- package/package.json +42 -0
- package/templates/minimal/README.md +132 -0
- package/templates/minimal/app/api/mdx-watch/route.ts +80 -0
- package/templates/minimal/app/docs/[version]/[...slug]/loading.tsx +7 -0
- package/templates/minimal/app/docs/[version]/[...slug]/page.tsx +212 -0
- package/templates/minimal/app/docs/[version]/not-found.tsx +10 -0
- package/templates/minimal/app/docs/[version]/page.tsx +27 -0
- package/templates/minimal/app/globals.css +1 -0
- package/templates/minimal/app/layout.tsx +89 -0
- package/templates/minimal/app/page.tsx +185 -0
- package/templates/minimal/docs/v1.0.0/about.mdx +57 -0
- package/templates/minimal/docs/v1.0.0/components/_category_.json +8 -0
- package/templates/minimal/docs/v1.0.0/components/callout.mdx +83 -0
- package/templates/minimal/docs/v1.0.0/components/code-block.mdx +103 -0
- package/templates/minimal/docs/v1.0.0/components/index.mdx +8 -0
- package/templates/minimal/docs/v1.0.0/components/tabs.mdx +92 -0
- package/templates/minimal/docs/v1.0.0/configuration.mdx +322 -0
- package/templates/minimal/docs/v1.0.0/features.mdx +197 -0
- package/templates/minimal/docs/v1.0.0/getting-started.mdx +183 -0
- package/templates/minimal/docs/v1.0.0/index.mdx +29 -0
- package/templates/minimal/middleware.ts +23 -0
- package/templates/minimal/next.config.default.mjs +36 -0
- package/templates/minimal/next.config.export.mjs +62 -0
- package/templates/minimal/next.config.mjs +18 -0
- package/templates/minimal/package-lock.json +7338 -0
- package/templates/minimal/package.json +32 -0
- package/templates/minimal/postcss.config.mjs +8 -0
- package/templates/minimal/public/api-specs/openapi-example.json +259 -0
- package/templates/minimal/public/api-specs/postman-example.json +205 -0
- package/templates/minimal/public/api-specs/test-api.json +256 -0
- package/templates/minimal/public/api-specs/users-api.json +264 -0
- package/templates/minimal/scripts/generate-redirects.mjs +88 -0
- package/templates/minimal/scripts/index-search.ts +159 -0
- package/templates/minimal/scripts/test-search.ts +83 -0
- package/templates/minimal/specra.config.json +124 -0
- package/templates/minimal/tsconfig.json +41 -0
- package/templates/minimal/yarn.lock +3909 -0
package/LICENSE.MD
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 dalmasonto, arthur-kamau
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# create-specra
|
|
2
|
+
|
|
3
|
+
The fastest way to create a new Specra documentation site. Scaffold a complete documentation project with a single command.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
### With npx (recommended)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx create-specra my-docs
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### With npm
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm create specra my-docs
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### With yarn
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
yarn create specra my-docs
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### With pnpm
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm create specra my-docs
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Options
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx create-specra [project-directory] [options]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Arguments
|
|
38
|
+
|
|
39
|
+
- `[project-directory]` - The directory to create the project in (optional, will prompt if not provided)
|
|
40
|
+
|
|
41
|
+
### Options
|
|
42
|
+
|
|
43
|
+
- `--template <template>` - Template to use: `minimal` (will prompt if not provided)
|
|
44
|
+
- `--use-npm` - Use npm as the package manager
|
|
45
|
+
- `--use-pnpm` - Use pnpm as the package manager
|
|
46
|
+
- `--use-yarn` - Use yarn as the package manager
|
|
47
|
+
- `--skip-install` - Skip package installation
|
|
48
|
+
|
|
49
|
+
## Templates
|
|
50
|
+
|
|
51
|
+
### Minimal (Default)
|
|
52
|
+
|
|
53
|
+
Minimal setup to get started quickly:
|
|
54
|
+
- Basic documentation structure
|
|
55
|
+
- Essential configuration
|
|
56
|
+
- Clean starting point
|
|
57
|
+
- Ready to customize
|
|
58
|
+
|
|
59
|
+
## Examples
|
|
60
|
+
|
|
61
|
+
Create a new project with interactive prompts:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx create-specra
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Create a project with the minimal template using npm:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npx create-specra my-docs --template minimal --use-npm
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Create a project and skip installation:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npx create-specra my-docs --skip-install
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## What's Created
|
|
80
|
+
|
|
81
|
+
The CLI creates a new Next.js project with Specra pre-configured:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
my-docs/
|
|
85
|
+
├── app/
|
|
86
|
+
│ ├── layout.tsx
|
|
87
|
+
│ ├── page.tsx
|
|
88
|
+
│ └── [...slug]/
|
|
89
|
+
│ └── page.tsx
|
|
90
|
+
├── docs/
|
|
91
|
+
│ └── v1.0.0/
|
|
92
|
+
│ └── index.mdx
|
|
93
|
+
├── public/
|
|
94
|
+
├── specra.config.ts
|
|
95
|
+
├── next.config.js
|
|
96
|
+
├── tailwind.config.ts
|
|
97
|
+
└── package.json
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## After Creation
|
|
101
|
+
|
|
102
|
+
Once your project is created, you can:
|
|
103
|
+
|
|
104
|
+
1. Start the development server:
|
|
105
|
+
```bash
|
|
106
|
+
cd my-docs
|
|
107
|
+
npm run dev
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
2. Open [http://localhost:3000](http://localhost:3000) in your browser
|
|
111
|
+
|
|
112
|
+
3. Edit your documentation in the `docs/` directory
|
|
113
|
+
|
|
114
|
+
4. Customize your site in `specra.config.ts`
|
|
115
|
+
|
|
116
|
+
## What is Specra?
|
|
117
|
+
|
|
118
|
+
Specra is a modern documentation library for Next.js that provides:
|
|
119
|
+
- Multi-version documentation support
|
|
120
|
+
- API reference generation
|
|
121
|
+
- Full-text search
|
|
122
|
+
- MDX-powered content
|
|
123
|
+
- Beautiful UI components
|
|
124
|
+
|
|
125
|
+
## Learn More
|
|
126
|
+
|
|
127
|
+
- [Specra on npm](https://www.npmjs.com/package/specra)
|
|
128
|
+
- [Next.js Documentation](https://nextjs.org/docs)
|
|
129
|
+
- [MDX Documentation](https://mdxjs.com)
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
MIT
|
|
134
|
+
|
|
135
|
+
## Authors
|
|
136
|
+
|
|
137
|
+
dalmasonto, arthur-kamau
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-specra",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI tool to scaffold a new Specra documentation site with Next.js",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-specra": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"templates"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsup",
|
|
15
|
+
"dev": "tsup --watch",
|
|
16
|
+
"typecheck": "tsc --noEmit"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"documentation",
|
|
20
|
+
"docs",
|
|
21
|
+
"nextjs",
|
|
22
|
+
"mdx",
|
|
23
|
+
"specra",
|
|
24
|
+
"create-app",
|
|
25
|
+
"cli"
|
|
26
|
+
],
|
|
27
|
+
"author": "dalmasonto, arthur-kamau",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"commander": "^12.1.0",
|
|
31
|
+
"picocolors": "^1.1.1",
|
|
32
|
+
"prompts": "^2.4.2",
|
|
33
|
+
"validate-npm-package-name": "^6.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22",
|
|
37
|
+
"@types/prompts": "^2.4.9",
|
|
38
|
+
"@types/validate-npm-package-name": "^4.0.2",
|
|
39
|
+
"tsup": "^8.3.5",
|
|
40
|
+
"typescript": "^5"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Specra Documentation Site
|
|
2
|
+
|
|
3
|
+
Welcome to your new Specra documentation site! This project was created with `create-specra`.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
First, install dependencies and run the development server:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
npm run dev
|
|
12
|
+
# or
|
|
13
|
+
yarn install
|
|
14
|
+
yarn dev
|
|
15
|
+
# or
|
|
16
|
+
pnpm install
|
|
17
|
+
pnpm dev
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Open [http://localhost:3000](http://localhost:3000) with your browser to see your documentation site.
|
|
21
|
+
|
|
22
|
+
## Project Structure
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
├── app/ # Next.js app directory
|
|
26
|
+
│ ├── layout.tsx # Root layout
|
|
27
|
+
│ ├── page.tsx # Home page
|
|
28
|
+
│ └── docs/ # Documentation pages
|
|
29
|
+
├── components/ # Reusable components
|
|
30
|
+
│ ├── docs/ # Documentation-specific components
|
|
31
|
+
│ └── ui/ # UI components
|
|
32
|
+
├── docs/ # Your MDX documentation files
|
|
33
|
+
│ └── v1.0.0/ # Version 1.0.0 docs
|
|
34
|
+
├── lib/ # Utility functions
|
|
35
|
+
│ ├── mdx.ts # MDX processing
|
|
36
|
+
│ ├── config.ts # Configuration
|
|
37
|
+
│ └── parsers/ # API parsers
|
|
38
|
+
├── public/ # Static assets
|
|
39
|
+
└── specra.config.json # Specra configuration
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Writing Documentation
|
|
43
|
+
|
|
44
|
+
Add your MDX files in the `docs/v1.0.0/` directory:
|
|
45
|
+
|
|
46
|
+
```mdx
|
|
47
|
+
---
|
|
48
|
+
title: My Page
|
|
49
|
+
description: This is my documentation page
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
# My Page
|
|
53
|
+
|
|
54
|
+
Your content here...
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Using Components
|
|
58
|
+
|
|
59
|
+
Import and use components in your MDX:
|
|
60
|
+
|
|
61
|
+
```mdx
|
|
62
|
+
import { Callout } from '@/components/docs/callout'
|
|
63
|
+
import { CodeBlock } from '@/components/docs/code-block'
|
|
64
|
+
import { Tabs, Tab } from '@/components/docs/tabs'
|
|
65
|
+
|
|
66
|
+
<Callout type="info">
|
|
67
|
+
This is an info callout!
|
|
68
|
+
</Callout>
|
|
69
|
+
|
|
70
|
+
<Tabs>
|
|
71
|
+
<Tab title="JavaScript">
|
|
72
|
+
```js
|
|
73
|
+
console.log('Hello World')
|
|
74
|
+
```
|
|
75
|
+
</Tab>
|
|
76
|
+
<Tab title="TypeScript">
|
|
77
|
+
```ts
|
|
78
|
+
console.log('Hello World')
|
|
79
|
+
```
|
|
80
|
+
</Tab>
|
|
81
|
+
</Tabs>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Configuration
|
|
85
|
+
|
|
86
|
+
Edit `specra.config.json` to customize your site:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"site": {
|
|
91
|
+
"title": "Your Docs",
|
|
92
|
+
"description": "Your documentation site",
|
|
93
|
+
"url": "https://yourdocs.com"
|
|
94
|
+
},
|
|
95
|
+
"navigation": {
|
|
96
|
+
"links": [
|
|
97
|
+
{ "title": "Home", "href": "/" },
|
|
98
|
+
{ "title": "Docs", "href": "/docs" }
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Building for Production
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npm run build
|
|
108
|
+
npm run start
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Learn More
|
|
112
|
+
|
|
113
|
+
- [Specra Documentation](https://specra.dev/docs)
|
|
114
|
+
- [Next.js Documentation](https://nextjs.org/docs)
|
|
115
|
+
- [MDX Documentation](https://mdxjs.com)
|
|
116
|
+
|
|
117
|
+
## Deployment
|
|
118
|
+
|
|
119
|
+
Deploy your Specra documentation site to Vercel, Netlify, or any hosting platform that supports Next.js.
|
|
120
|
+
|
|
121
|
+
### Vercel
|
|
122
|
+
|
|
123
|
+
[](https://vercel.com/new)
|
|
124
|
+
|
|
125
|
+
### Netlify
|
|
126
|
+
|
|
127
|
+
[](https://app.netlify.com/start)
|
|
128
|
+
|
|
129
|
+
## Need Help?
|
|
130
|
+
|
|
131
|
+
- Check the [documentation](https://specra.dev/docs)
|
|
132
|
+
- Report issues on [GitHub](https://github.com/yourusername/specra/issues)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { NextRequest } from 'next/server'
|
|
2
|
+
import { watch } from 'fs'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
|
|
5
|
+
export const dynamic = 'force-static'
|
|
6
|
+
|
|
7
|
+
export async function GET(request: NextRequest) {
|
|
8
|
+
// Only allow in development mode
|
|
9
|
+
if (process.env.NODE_ENV !== 'development') {
|
|
10
|
+
return new Response('Not available in production', { status: 404 })
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const encoder = new TextEncoder()
|
|
14
|
+
|
|
15
|
+
const stream = new ReadableStream({
|
|
16
|
+
start(controller) {
|
|
17
|
+
// Send initial connection message
|
|
18
|
+
const connectMsg = `data: ${JSON.stringify({ type: 'connected' })}\n\n`
|
|
19
|
+
controller.enqueue(encoder.encode(connectMsg))
|
|
20
|
+
|
|
21
|
+
const docsPath = join(process.cwd(), 'docs')
|
|
22
|
+
|
|
23
|
+
// Watch the docs directory recursively
|
|
24
|
+
const watcher = watch(
|
|
25
|
+
docsPath,
|
|
26
|
+
{ recursive: true },
|
|
27
|
+
(eventType, filename) => {
|
|
28
|
+
if (!filename) return
|
|
29
|
+
|
|
30
|
+
// Only watch for .mdx and .json files (MDX files and category configs)
|
|
31
|
+
if (filename.endsWith('.mdx') || filename.endsWith('.json')) {
|
|
32
|
+
console.log(`[MDX Watch] ${eventType}: ${filename}`)
|
|
33
|
+
|
|
34
|
+
const message = `data: ${JSON.stringify({
|
|
35
|
+
type: 'change',
|
|
36
|
+
file: filename,
|
|
37
|
+
eventType
|
|
38
|
+
})}\n\n`
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
controller.enqueue(encoder.encode(message))
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error('[MDX Watch] Error sending message:', error)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
// Handle client disconnect
|
|
50
|
+
request.signal.addEventListener('abort', () => {
|
|
51
|
+
console.log('[MDX Watch] Client disconnected')
|
|
52
|
+
watcher.close()
|
|
53
|
+
try {
|
|
54
|
+
controller.close()
|
|
55
|
+
} catch (error) {
|
|
56
|
+
// Controller might already be closed
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Handle errors
|
|
61
|
+
watcher.on('error', (error) => {
|
|
62
|
+
console.error('[MDX Watch] Watcher error:', error)
|
|
63
|
+
watcher.close()
|
|
64
|
+
try {
|
|
65
|
+
controller.close()
|
|
66
|
+
} catch (e) {
|
|
67
|
+
// Controller might already be closed
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
return new Response(stream, {
|
|
74
|
+
headers: {
|
|
75
|
+
'Content-Type': 'text/event-stream',
|
|
76
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
77
|
+
'Connection': 'keep-alive',
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import type { Metadata } from "next"
|
|
2
|
+
import { Suspense } from "react"
|
|
3
|
+
import {
|
|
4
|
+
extractTableOfContents,
|
|
5
|
+
getAdjacentDocs,
|
|
6
|
+
isCategoryPage,
|
|
7
|
+
getCachedVersions,
|
|
8
|
+
getCachedAllDocs,
|
|
9
|
+
getCachedDocBySlug,
|
|
10
|
+
getConfig,
|
|
11
|
+
SpecraConfig,
|
|
12
|
+
} from "specra/lib"
|
|
13
|
+
import {
|
|
14
|
+
DocLayout,
|
|
15
|
+
TableOfContents,
|
|
16
|
+
Header,
|
|
17
|
+
DocLayoutWrapper,
|
|
18
|
+
HotReloadIndicator,
|
|
19
|
+
DevModeBadge,
|
|
20
|
+
MdxHotReload,
|
|
21
|
+
CategoryIndex,
|
|
22
|
+
NotFoundContent,
|
|
23
|
+
DocLoading,
|
|
24
|
+
} from "specra/components"
|
|
25
|
+
|
|
26
|
+
import specraConfig from "./../../../../specra.config.json"
|
|
27
|
+
|
|
28
|
+
interface PageProps {
|
|
29
|
+
params: Promise<{
|
|
30
|
+
version: string
|
|
31
|
+
slug: string[]
|
|
32
|
+
}>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
|
|
36
|
+
const { version, slug: slugArray } = await params
|
|
37
|
+
const slug = slugArray.join("/")
|
|
38
|
+
|
|
39
|
+
const doc = await getCachedDocBySlug(slug, version)
|
|
40
|
+
|
|
41
|
+
if (!doc) {
|
|
42
|
+
return {
|
|
43
|
+
title: "Page Not Found",
|
|
44
|
+
description: "The requested documentation page could not be found.",
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const title = doc.meta.title || doc.title
|
|
49
|
+
const description = doc.meta.description || `Documentation for ${title}`
|
|
50
|
+
const url = `/docs/${version}/${slug}`
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
title: `${title}`,
|
|
54
|
+
description,
|
|
55
|
+
openGraph: {
|
|
56
|
+
title,
|
|
57
|
+
description,
|
|
58
|
+
url,
|
|
59
|
+
siteName: "Documentation Platform",
|
|
60
|
+
type: "article",
|
|
61
|
+
locale: "en_US",
|
|
62
|
+
},
|
|
63
|
+
twitter: {
|
|
64
|
+
card: "summary_large_image",
|
|
65
|
+
title,
|
|
66
|
+
description,
|
|
67
|
+
},
|
|
68
|
+
alternates: {
|
|
69
|
+
canonical: url,
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function generateStaticParams() {
|
|
75
|
+
const versions = getCachedVersions()
|
|
76
|
+
const params = []
|
|
77
|
+
|
|
78
|
+
for (const version of versions) {
|
|
79
|
+
const docs = await getCachedAllDocs(version)
|
|
80
|
+
for (const doc of docs) {
|
|
81
|
+
// Add the custom slug path
|
|
82
|
+
params.push({
|
|
83
|
+
version,
|
|
84
|
+
slug: doc.slug.split("/").filter(Boolean),
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return params
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export default async function DocPage({ params }: PageProps) {
|
|
93
|
+
const { version, slug: slugArray } = await params
|
|
94
|
+
const slug = slugArray.join("/")
|
|
95
|
+
|
|
96
|
+
const allDocs = await getCachedAllDocs(version)
|
|
97
|
+
const versions = getCachedVersions()
|
|
98
|
+
const config = getConfig()
|
|
99
|
+
const isCategory = isCategoryPage(slug, allDocs)
|
|
100
|
+
|
|
101
|
+
// Try to get the doc (might be index.mdx or regular .mdx)
|
|
102
|
+
const doc = await getCachedDocBySlug(slug, version)
|
|
103
|
+
|
|
104
|
+
// If no doc found and it's a category, show category index
|
|
105
|
+
if (!doc && isCategory) {
|
|
106
|
+
// Find a doc in this category to get the tab group
|
|
107
|
+
const categoryDoc = allDocs.find((d) => d.slug.startsWith(slug + "/"))
|
|
108
|
+
const categoryTabGroup = categoryDoc?.meta?.tab_group || categoryDoc?.categoryTabGroup
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<>
|
|
112
|
+
<DocLayoutWrapper
|
|
113
|
+
header={<Header currentVersion={version} versions={versions} config={config} />}
|
|
114
|
+
docs={allDocs}
|
|
115
|
+
version={version}
|
|
116
|
+
content={
|
|
117
|
+
<CategoryIndex
|
|
118
|
+
categoryPath={slug}
|
|
119
|
+
version={version}
|
|
120
|
+
allDocs={allDocs}
|
|
121
|
+
title={slug.split("/").pop()?.replace(/-/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()) || "Category"}
|
|
122
|
+
description="Browse the documentation in this section."
|
|
123
|
+
config={config}
|
|
124
|
+
/>
|
|
125
|
+
}
|
|
126
|
+
toc={<div />}
|
|
127
|
+
config={config}
|
|
128
|
+
currentPageTabGroup={categoryTabGroup}
|
|
129
|
+
/>
|
|
130
|
+
<MdxHotReload />
|
|
131
|
+
<HotReloadIndicator />
|
|
132
|
+
<DevModeBadge />
|
|
133
|
+
</>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// If no doc found, render 404 content within the layout (keeps sidebar visible)
|
|
138
|
+
if (!doc) {
|
|
139
|
+
return (
|
|
140
|
+
<>
|
|
141
|
+
<Suspense fallback={<DocLoading />}>
|
|
142
|
+
<DocLayoutWrapper
|
|
143
|
+
header={<Header currentVersion={version} versions={versions} config={config} />}
|
|
144
|
+
docs={allDocs}
|
|
145
|
+
version={version}
|
|
146
|
+
content={<NotFoundContent version={version} />}
|
|
147
|
+
toc={<div />}
|
|
148
|
+
config={config}
|
|
149
|
+
currentPageTabGroup={undefined}
|
|
150
|
+
/>
|
|
151
|
+
<MdxHotReload />
|
|
152
|
+
<HotReloadIndicator />
|
|
153
|
+
<DevModeBadge />
|
|
154
|
+
</Suspense>
|
|
155
|
+
</>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const toc = extractTableOfContents(doc.content)
|
|
160
|
+
const { previous, next } = getAdjacentDocs(slug, allDocs)
|
|
161
|
+
|
|
162
|
+
// console.log("[v0] Extracted ToC:", toc)
|
|
163
|
+
|
|
164
|
+
// If doc exists but is also a category, show both content and children
|
|
165
|
+
const showCategoryIndex = isCategory && doc
|
|
166
|
+
|
|
167
|
+
// console.log("showCategoryIndex: ", showCategoryIndex)
|
|
168
|
+
|
|
169
|
+
// Get current page's tab group from doc metadata or category
|
|
170
|
+
const currentPageTabGroup = doc.meta?.tab_group || doc.categoryTabGroup
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<>
|
|
174
|
+
<Suspense fallback={<DocLoading />}>
|
|
175
|
+
<DocLayoutWrapper
|
|
176
|
+
header={<Header currentVersion={version} versions={versions} config={config} />}
|
|
177
|
+
docs={allDocs}
|
|
178
|
+
version={version}
|
|
179
|
+
content={
|
|
180
|
+
showCategoryIndex ? (
|
|
181
|
+
<CategoryIndex
|
|
182
|
+
categoryPath={slug}
|
|
183
|
+
version={version}
|
|
184
|
+
allDocs={allDocs}
|
|
185
|
+
title={doc.meta.title}
|
|
186
|
+
description={doc.meta.description}
|
|
187
|
+
content={doc.content}
|
|
188
|
+
config={config}
|
|
189
|
+
/>
|
|
190
|
+
) : (
|
|
191
|
+
<DocLayout
|
|
192
|
+
meta={doc.meta}
|
|
193
|
+
content={doc.content}
|
|
194
|
+
previousDoc={previous ? { title: previous.meta.title, slug: previous.slug } : undefined}
|
|
195
|
+
nextDoc={next ? { title: next.meta.title, slug: next.slug } : undefined}
|
|
196
|
+
version={version}
|
|
197
|
+
slug={slug}
|
|
198
|
+
config={config}
|
|
199
|
+
/>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
toc={showCategoryIndex ? <div /> : <TableOfContents items={toc} config={config} />}
|
|
203
|
+
config={config}
|
|
204
|
+
currentPageTabGroup={currentPageTabGroup}
|
|
205
|
+
/>
|
|
206
|
+
<MdxHotReload />
|
|
207
|
+
<HotReloadIndicator />
|
|
208
|
+
<DevModeBadge />
|
|
209
|
+
</Suspense>
|
|
210
|
+
</>
|
|
211
|
+
)
|
|
212
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { redirect } from "next/navigation"
|
|
2
|
+
import { getCachedVersions, getCachedAllDocs } from "specra/lib"
|
|
3
|
+
|
|
4
|
+
interface PageProps {
|
|
5
|
+
params: Promise<{
|
|
6
|
+
version: string
|
|
7
|
+
}>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function generateStaticParams() {
|
|
11
|
+
const versions = getCachedVersions()
|
|
12
|
+
return versions.map((version) => ({
|
|
13
|
+
version,
|
|
14
|
+
}))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default async function VersionIndexPage({ params }: PageProps) {
|
|
18
|
+
const { version } = await params
|
|
19
|
+
const docs = await getCachedAllDocs(version)
|
|
20
|
+
|
|
21
|
+
if (docs.length === 0) {
|
|
22
|
+
redirect("/docs/v1.0.0")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Redirect to first doc
|
|
26
|
+
redirect(`/docs/${version}/${docs[0].slug}`)
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import "specra/styles";
|