specra-cli 0.3.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 +33 -0
- package/README.md +246 -0
- package/dist/api-client-VHQARPDT.js +15 -0
- package/dist/api-client-VHQARPDT.js.map +1 -0
- package/dist/chunk-5765WX4D.js +192 -0
- package/dist/chunk-5765WX4D.js.map +1 -0
- package/dist/chunk-72RDEJR2.js +94 -0
- package/dist/chunk-72RDEJR2.js.map +1 -0
- package/dist/chunk-SQ2MMFUZ.js +102 -0
- package/dist/chunk-SQ2MMFUZ.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +242 -0
- package/dist/cli.js.map +1 -0
- package/dist/deploy-V4JO2D6B.js +179 -0
- package/dist/deploy-V4JO2D6B.js.map +1 -0
- package/dist/doctor-ICALAJ4N.js +309 -0
- package/dist/doctor-ICALAJ4N.js.map +1 -0
- package/dist/login-UG3WU7DY.js +92 -0
- package/dist/login-UG3WU7DY.js.map +1 -0
- package/dist/logout-WJKHJZT6.js +24 -0
- package/dist/logout-WJKHJZT6.js.map +1 -0
- package/dist/logs-BLUJPWNO.js +77 -0
- package/dist/logs-BLUJPWNO.js.map +1 -0
- package/dist/projects-LJ57GK3D.js +49 -0
- package/dist/projects-LJ57GK3D.js.map +1 -0
- package/package.json +50 -0
- package/templates/book-docs/.env.sample +1 -0
- package/templates/book-docs/docs/v1.0.0/concepts.mdx +89 -0
- package/templates/book-docs/docs/v1.0.0/content/_category_.json +7 -0
- package/templates/book-docs/docs/v1.0.0/content/formatting.mdx +128 -0
- package/templates/book-docs/docs/v1.0.0/content/reusable-content.mdx +116 -0
- package/templates/book-docs/docs/v1.0.0/content/structure.mdx +92 -0
- package/templates/book-docs/docs/v1.0.0/customization/_category_.json +7 -0
- package/templates/book-docs/docs/v1.0.0/customization/branding.mdx +115 -0
- package/templates/book-docs/docs/v1.0.0/customization/themes.mdx +81 -0
- package/templates/book-docs/docs/v1.0.0/introduction.mdx +38 -0
- package/templates/book-docs/docs/v1.0.0/quickstart.mdx +112 -0
- package/templates/book-docs/docs/v2.0.0/concepts.mdx +89 -0
- package/templates/book-docs/docs/v2.0.0/content/_category_.json +7 -0
- package/templates/book-docs/docs/v2.0.0/content/formatting.mdx +128 -0
- package/templates/book-docs/docs/v2.0.0/content/reusable-content.mdx +116 -0
- package/templates/book-docs/docs/v2.0.0/content/structure.mdx +92 -0
- package/templates/book-docs/docs/v2.0.0/customization/_category_.json +7 -0
- package/templates/book-docs/docs/v2.0.0/customization/branding.mdx +115 -0
- package/templates/book-docs/docs/v2.0.0/customization/themes.mdx +81 -0
- package/templates/book-docs/docs/v2.0.0/introduction.mdx +39 -0
- package/templates/book-docs/docs/v2.0.0/quickstart.mdx +112 -0
- package/templates/book-docs/gitignore +7 -0
- package/templates/book-docs/package.json +28 -0
- package/templates/book-docs/postcss.config.mjs +8 -0
- package/templates/book-docs/public/api-specs/openapi-example.json +259 -0
- package/templates/book-docs/public/api-specs/postman-example.json +205 -0
- package/templates/book-docs/public/api-specs/test-api.json +256 -0
- package/templates/book-docs/public/api-specs/users-api.json +264 -0
- package/templates/book-docs/specra.config.json +77 -0
- package/templates/book-docs/src/app.css +86 -0
- package/templates/book-docs/src/app.html +17 -0
- package/templates/book-docs/src/params/product.ts +7 -0
- package/templates/book-docs/src/routes/+layout.server.ts +14 -0
- package/templates/book-docs/src/routes/+layout.svelte +21 -0
- package/templates/book-docs/src/routes/+page.server.ts +9 -0
- package/templates/book-docs/src/routes/docs/[product=product]/[version]/+layout.server.ts +40 -0
- package/templates/book-docs/src/routes/docs/[product=product]/[version]/+page.server.ts +24 -0
- package/templates/book-docs/src/routes/docs/[product=product]/[version]/[...slug]/+page.server.ts +131 -0
- package/templates/book-docs/src/routes/docs/[product=product]/[version]/[...slug]/+page.svelte +180 -0
- package/templates/book-docs/src/routes/docs/[version]/+layout.server.ts +42 -0
- package/templates/book-docs/src/routes/docs/[version]/+page.server.ts +27 -0
- package/templates/book-docs/src/routes/docs/[version]/[...slug]/+page.server.ts +106 -0
- package/templates/book-docs/src/routes/docs/[version]/[...slug]/+page.svelte +172 -0
- package/templates/book-docs/static/favicon.svg +4 -0
- package/templates/book-docs/svelte.config.js +13 -0
- package/templates/book-docs/tsconfig.json +12 -0
- package/templates/book-docs/vite.config.ts +6 -0
- package/templates/jbrains-docs/.env.sample +1 -0
- package/templates/jbrains-docs/docs/v1.0.0/advanced/_category_.json +8 -0
- package/templates/jbrains-docs/docs/v1.0.0/advanced/async.mdx +95 -0
- package/templates/jbrains-docs/docs/v1.0.0/advanced/generics.mdx +126 -0
- package/templates/jbrains-docs/docs/v1.0.0/basics/_category_.json +8 -0
- package/templates/jbrains-docs/docs/v1.0.0/basics/control-flow.mdx +106 -0
- package/templates/jbrains-docs/docs/v1.0.0/basics/syntax.mdx +129 -0
- package/templates/jbrains-docs/docs/v1.0.0/basics/types.mdx +135 -0
- package/templates/jbrains-docs/docs/v1.0.0/getting-started.mdx +111 -0
- package/templates/jbrains-docs/docs/v1.0.0/home.mdx +37 -0
- package/templates/jbrains-docs/docs/v1.0.0/tools/_category_.json +8 -0
- package/templates/jbrains-docs/docs/v1.0.0/tools/build-tools.mdx +165 -0
- package/templates/jbrains-docs/docs/v1.0.0/tools/testing.mdx +112 -0
- package/templates/jbrains-docs/docs/v2.0.0/advanced/_category_.json +8 -0
- package/templates/jbrains-docs/docs/v2.0.0/advanced/async.mdx +95 -0
- package/templates/jbrains-docs/docs/v2.0.0/advanced/generics.mdx +126 -0
- package/templates/jbrains-docs/docs/v2.0.0/basics/_category_.json +8 -0
- package/templates/jbrains-docs/docs/v2.0.0/basics/control-flow.mdx +106 -0
- package/templates/jbrains-docs/docs/v2.0.0/basics/syntax.mdx +129 -0
- package/templates/jbrains-docs/docs/v2.0.0/basics/types.mdx +135 -0
- package/templates/jbrains-docs/docs/v2.0.0/getting-started.mdx +111 -0
- package/templates/jbrains-docs/docs/v2.0.0/home.mdx +37 -0
- package/templates/jbrains-docs/docs/v2.0.0/tools/_category_.json +8 -0
- package/templates/jbrains-docs/docs/v2.0.0/tools/build-tools.mdx +165 -0
- package/templates/jbrains-docs/docs/v2.0.0/tools/testing.mdx +112 -0
- package/templates/jbrains-docs/gitignore +7 -0
- package/templates/jbrains-docs/package.json +28 -0
- package/templates/jbrains-docs/postcss.config.mjs +8 -0
- package/templates/jbrains-docs/public/api-specs/openapi-example.json +259 -0
- package/templates/jbrains-docs/public/api-specs/postman-example.json +205 -0
- package/templates/jbrains-docs/public/api-specs/test-api.json +256 -0
- package/templates/jbrains-docs/public/api-specs/users-api.json +264 -0
- package/templates/jbrains-docs/specra.config.json +80 -0
- package/templates/jbrains-docs/src/app.css +86 -0
- package/templates/jbrains-docs/src/app.html +17 -0
- package/templates/jbrains-docs/src/params/product.ts +7 -0
- package/templates/jbrains-docs/src/routes/+layout.server.ts +14 -0
- package/templates/jbrains-docs/src/routes/+layout.svelte +21 -0
- package/templates/jbrains-docs/src/routes/+page.server.ts +9 -0
- package/templates/jbrains-docs/src/routes/docs/[product=product]/[version]/+layout.server.ts +40 -0
- package/templates/jbrains-docs/src/routes/docs/[product=product]/[version]/+page.server.ts +24 -0
- package/templates/jbrains-docs/src/routes/docs/[product=product]/[version]/[...slug]/+page.server.ts +131 -0
- package/templates/jbrains-docs/src/routes/docs/[product=product]/[version]/[...slug]/+page.svelte +180 -0
- package/templates/jbrains-docs/src/routes/docs/[version]/+layout.server.ts +42 -0
- package/templates/jbrains-docs/src/routes/docs/[version]/+page.server.ts +27 -0
- package/templates/jbrains-docs/src/routes/docs/[version]/[...slug]/+page.server.ts +106 -0
- package/templates/jbrains-docs/src/routes/docs/[version]/[...slug]/+page.svelte +172 -0
- package/templates/jbrains-docs/static/favicon.svg +4 -0
- package/templates/jbrains-docs/svelte.config.js +13 -0
- package/templates/jbrains-docs/tsconfig.json +12 -0
- package/templates/jbrains-docs/vite.config.ts +6 -0
- package/templates/minimal/.env.sample +1 -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/v2.0.0/about.mdx +57 -0
- package/templates/minimal/docs/v2.0.0/components/_category_.json +8 -0
- package/templates/minimal/docs/v2.0.0/components/callout.mdx +83 -0
- package/templates/minimal/docs/v2.0.0/components/code-block.mdx +103 -0
- package/templates/minimal/docs/v2.0.0/components/index.mdx +8 -0
- package/templates/minimal/docs/v2.0.0/components/tabs.mdx +92 -0
- package/templates/minimal/docs/v2.0.0/configuration.mdx +322 -0
- package/templates/minimal/docs/v2.0.0/features.mdx +197 -0
- package/templates/minimal/docs/v2.0.0/getting-started.mdx +183 -0
- package/templates/minimal/gitignore +7 -0
- package/templates/minimal/package.json +29 -0
- package/templates/minimal/postcss.config.mjs +8 -0
- package/templates/minimal/specra.config.json +91 -0
- package/templates/minimal/src/app.css +86 -0
- package/templates/minimal/src/app.html +17 -0
- package/templates/minimal/src/hooks.server.ts +8 -0
- package/templates/minimal/src/params/product.ts +7 -0
- package/templates/minimal/src/routes/+error.svelte +10 -0
- package/templates/minimal/src/routes/+layout.server.ts +14 -0
- package/templates/minimal/src/routes/+layout.svelte +21 -0
- package/templates/minimal/src/routes/+page.server.ts +9 -0
- package/templates/minimal/src/routes/+page.svelte +149 -0
- package/templates/minimal/src/routes/docs/[product=product]/[version]/+layout.server.ts +40 -0
- package/templates/minimal/src/routes/docs/[product=product]/[version]/+page.server.ts +24 -0
- package/templates/minimal/src/routes/docs/[product=product]/[version]/[...slug]/+page.server.ts +131 -0
- package/templates/minimal/src/routes/docs/[product=product]/[version]/[...slug]/+page.svelte +180 -0
- package/templates/minimal/src/routes/docs/[version]/+layout.server.ts +42 -0
- package/templates/minimal/src/routes/docs/[version]/+page.server.ts +27 -0
- package/templates/minimal/src/routes/docs/[version]/[...slug]/+page.server.ts +106 -0
- package/templates/minimal/src/routes/docs/[version]/[...slug]/+page.svelte +172 -0
- package/templates/minimal/static/api-specs/openapi-example.json +259 -0
- package/templates/minimal/static/api-specs/postman-example.json +205 -0
- package/templates/minimal/static/api-specs/test-api.json +256 -0
- package/templates/minimal/static/api-specs/users-api.json +264 -0
- package/templates/minimal/static/favicon.svg +4 -0
- package/templates/minimal/svelte.config.js +13 -0
- package/templates/minimal/tsconfig.json +12 -0
- package/templates/minimal/vite.config.ts +6 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { ArrowRight, BookOpen, Zap, Code, Github, Twitter, Linkedin } from 'lucide-svelte';
|
|
3
|
+
import { Button, SiteBanner, Logo } from 'specra/components';
|
|
4
|
+
|
|
5
|
+
let { data } = $props();
|
|
6
|
+
const config = data.config;
|
|
7
|
+
const activeVersion = config.site.activeVersion || 'v1.0.0';
|
|
8
|
+
const docsUrl = `/docs/${activeVersion}/about`;
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<svelte:head>
|
|
12
|
+
<title>{config.site.title}</title>
|
|
13
|
+
<meta name="description" content={config.site.description || 'Modern documentation platform'} />
|
|
14
|
+
</svelte:head>
|
|
15
|
+
|
|
16
|
+
<div class="min-h-screen bg-background">
|
|
17
|
+
<SiteBanner {config} />
|
|
18
|
+
<header class="border-b border-border">
|
|
19
|
+
<div class="container flex h-16 items-center justify-between px-6 mx-auto">
|
|
20
|
+
<a href="/" class="flex items-center gap-2">
|
|
21
|
+
<Logo logo={config.site.logo} alt={config.site.title} class="w-18 object-contain" />
|
|
22
|
+
<span class="font-semibold text-lg text-foreground">Specra</span>
|
|
23
|
+
</a>
|
|
24
|
+
<div class="flex items-center gap-6">
|
|
25
|
+
<a href={docsUrl} class="text-sm text-muted-foreground hover:text-foreground transition-colors">
|
|
26
|
+
Documentation
|
|
27
|
+
</a>
|
|
28
|
+
{#if config?.social?.github}
|
|
29
|
+
<a href={config.social.github} target="_blank" rel="noopener noreferrer" class="text-muted-foreground hover:text-foreground transition-colors">
|
|
30
|
+
<Github class="h-5 w-5" />
|
|
31
|
+
</a>
|
|
32
|
+
{/if}
|
|
33
|
+
<Button href={docsUrl}>Get Started</Button>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</header>
|
|
37
|
+
|
|
38
|
+
<main class="container px-6 mx-auto">
|
|
39
|
+
<!-- Hero Section -->
|
|
40
|
+
<div class="mx-auto text-center space-y-6 py-20 max-w-4xl">
|
|
41
|
+
<h1 class="text-5xl md:text-6xl font-bold tracking-tight text-foreground">
|
|
42
|
+
Beautiful documentation made <span class="text-primary">simple</span>
|
|
43
|
+
</h1>
|
|
44
|
+
<p class="text-xl md:text-2xl text-muted-foreground leading-relaxed max-w-3xl mx-auto">
|
|
45
|
+
The modern documentation framework that grows with your project
|
|
46
|
+
</p>
|
|
47
|
+
<p class="text-base md:text-lg text-muted-foreground leading-relaxed max-w-2xl mx-auto">
|
|
48
|
+
Built for developers who want powerful, flexible documentation without the complexity.
|
|
49
|
+
Write in MDX, configure with ease, and ship beautiful docs in minutes.
|
|
50
|
+
</p>
|
|
51
|
+
<div class="flex items-center justify-center gap-4 pt-4">
|
|
52
|
+
<Button href={docsUrl} size="lg">
|
|
53
|
+
Get Started
|
|
54
|
+
<ArrowRight class="ml-2 h-4 w-4" />
|
|
55
|
+
</Button>
|
|
56
|
+
{#if config?.social?.github}
|
|
57
|
+
<Button href={config.social.github} size="lg" variant="outline">
|
|
58
|
+
<Github class="mr-2 h-4 w-4" />
|
|
59
|
+
View on GitHub
|
|
60
|
+
</Button>
|
|
61
|
+
{/if}
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<!-- Key Features -->
|
|
66
|
+
<div class="grid md:grid-cols-3 gap-6 py-16 max-w-6xl mx-auto">
|
|
67
|
+
<div class="p-6 rounded-lg border border-border bg-card hover:shadow-md transition-shadow">
|
|
68
|
+
<div class="h-12 w-12 rounded-lg bg-primary/10 flex items-center justify-center mb-4">
|
|
69
|
+
<BookOpen class="h-6 w-6 text-primary" />
|
|
70
|
+
</div>
|
|
71
|
+
<h3 class="text-lg font-semibold text-foreground mb-2">MDX-Powered</h3>
|
|
72
|
+
<p class="text-sm text-muted-foreground leading-relaxed">
|
|
73
|
+
Write your docs in MDX with support for custom components, code blocks with syntax highlighting, and rich interactive content.
|
|
74
|
+
</p>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div class="p-6 rounded-lg border border-border bg-card hover:shadow-md transition-shadow">
|
|
78
|
+
<div class="h-12 w-12 rounded-lg bg-primary/10 flex items-center justify-center mb-4">
|
|
79
|
+
<Zap class="h-6 w-6 text-primary" />
|
|
80
|
+
</div>
|
|
81
|
+
<h3 class="text-lg font-semibold text-foreground mb-2">Lightning Fast</h3>
|
|
82
|
+
<p class="text-sm text-muted-foreground leading-relaxed">
|
|
83
|
+
Built on SvelteKit for optimal performance. Server-side rendering, instant page loads, and seamless navigation out of the box.
|
|
84
|
+
</p>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div class="p-6 rounded-lg border border-border bg-card hover:shadow-md transition-shadow">
|
|
88
|
+
<div class="h-12 w-12 rounded-lg bg-primary/10 flex items-center justify-center mb-4">
|
|
89
|
+
<Code class="h-6 w-6 text-primary" />
|
|
90
|
+
</div>
|
|
91
|
+
<h3 class="text-lg font-semibold text-foreground mb-2">Developer First</h3>
|
|
92
|
+
<p class="text-sm text-muted-foreground leading-relaxed">
|
|
93
|
+
Version control friendly, Git-based workflow, and full TypeScript support. Your docs live alongside your code.
|
|
94
|
+
</p>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<!-- CTA Section -->
|
|
99
|
+
<div class="py-16 max-w-4xl mx-auto">
|
|
100
|
+
<div class="rounded-xl border border-border bg-card p-8 md:p-12 text-center space-y-6">
|
|
101
|
+
<h2 class="text-3xl md:text-4xl font-bold text-foreground">Ready to Get Started?</h2>
|
|
102
|
+
<p class="text-muted-foreground max-w-2xl mx-auto">
|
|
103
|
+
Create beautiful, versioned documentation for your project in minutes.<br />
|
|
104
|
+
Specra gives you everything you need to write, organize, and publish great docs.
|
|
105
|
+
</p>
|
|
106
|
+
<div class="flex flex-col sm:flex-row items-center justify-center gap-4 pt-2">
|
|
107
|
+
<Button href={docsUrl} size="lg" variant="default">
|
|
108
|
+
Read the Docs
|
|
109
|
+
<ArrowRight class="ml-2 h-4 w-4" />
|
|
110
|
+
</Button>
|
|
111
|
+
{#if config?.social?.github}
|
|
112
|
+
<Button href={config.social.github} size="lg" variant="outline">
|
|
113
|
+
<Github class="mr-2 h-4 w-4" />
|
|
114
|
+
Star on GitHub
|
|
115
|
+
</Button>
|
|
116
|
+
{/if}
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<!-- Community Section -->
|
|
122
|
+
<div class="py-16 max-w-3xl mx-auto text-center space-y-6">
|
|
123
|
+
<h2 class="text-2xl md:text-3xl font-bold text-foreground">Join the Community</h2>
|
|
124
|
+
<p class="text-muted-foreground">
|
|
125
|
+
Specra is open source and community-driven. Connect with developers and stay updated.
|
|
126
|
+
</p>
|
|
127
|
+
<div class="flex items-center justify-center gap-4 pt-4">
|
|
128
|
+
{#if config?.social?.github}
|
|
129
|
+
<Button href={config.social.github} variant="outline" size="lg">
|
|
130
|
+
<Github class="mr-2 h-5 w-5" />
|
|
131
|
+
GitHub
|
|
132
|
+
</Button>
|
|
133
|
+
{/if}
|
|
134
|
+
{#if config?.social?.twitter}
|
|
135
|
+
<Button href={config.social.twitter} variant="outline" size="lg">
|
|
136
|
+
<Twitter class="mr-2 h-5 w-5" />
|
|
137
|
+
Twitter
|
|
138
|
+
</Button>
|
|
139
|
+
{/if}
|
|
140
|
+
{#if config?.social?.linkedin}
|
|
141
|
+
<Button href={config.social.linkedin} variant="outline" size="lg">
|
|
142
|
+
<Linkedin class="mr-2 h-5 w-5" />
|
|
143
|
+
LinkedIn
|
|
144
|
+
</Button>
|
|
145
|
+
{/if}
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</main>
|
|
149
|
+
</div>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getCachedVersions, getCachedAllDocs, getEffectiveConfig, getI18nConfig, getVersionsMeta, getProducts, loadVersionConfig } from 'specra';
|
|
2
|
+
import { redirect } from '@sveltejs/kit';
|
|
3
|
+
import type { LayoutServerLoad } from './$types';
|
|
4
|
+
|
|
5
|
+
export const load: LayoutServerLoad = async ({ params }) => {
|
|
6
|
+
const { product, version } = params;
|
|
7
|
+
|
|
8
|
+
// Verify this is a valid product — if not, fall through to 404
|
|
9
|
+
const products = getProducts();
|
|
10
|
+
const matchedProduct = products.find(p => p.slug === product);
|
|
11
|
+
if (!matchedProduct) {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const i18nConfig = getI18nConfig();
|
|
16
|
+
const defaultLocale = i18nConfig?.defaultLocale || 'en';
|
|
17
|
+
|
|
18
|
+
// Block access to hidden versions — redirect to product's active version
|
|
19
|
+
const currentVersionConfig = loadVersionConfig(version, product);
|
|
20
|
+
if (currentVersionConfig?.hidden) {
|
|
21
|
+
const config = getEffectiveConfig(version, product);
|
|
22
|
+
const activeVersion = matchedProduct.config.activeVersion || config.site?.activeVersion || 'v1.0.0';
|
|
23
|
+
throw redirect(302, `/docs/${product}/${activeVersion}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const allDocs = await getCachedAllDocs(version, defaultLocale, product);
|
|
27
|
+
const versions = getCachedVersions(product);
|
|
28
|
+
const config = getEffectiveConfig(version, product);
|
|
29
|
+
const versionsMeta = getVersionsMeta(versions, product);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
allDocs,
|
|
33
|
+
versions,
|
|
34
|
+
versionsMeta,
|
|
35
|
+
config,
|
|
36
|
+
product,
|
|
37
|
+
products,
|
|
38
|
+
versionBanner: currentVersionConfig?.banner,
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { redirect } from '@sveltejs/kit';
|
|
2
|
+
import { getCachedAllDocs, getProducts } from 'specra';
|
|
3
|
+
import type { PageServerLoad } from './$types';
|
|
4
|
+
|
|
5
|
+
export const load: PageServerLoad = async ({ params }) => {
|
|
6
|
+
const { product, version } = params;
|
|
7
|
+
|
|
8
|
+
// Verify product exists
|
|
9
|
+
const products = getProducts();
|
|
10
|
+
const matchedProduct = products.find(p => p.slug === product);
|
|
11
|
+
if (!matchedProduct) {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const docs = await getCachedAllDocs(version, undefined, product);
|
|
16
|
+
|
|
17
|
+
if (docs.length === 0) {
|
|
18
|
+
const activeVersion = matchedProduct.config.activeVersion || 'v1.0.0';
|
|
19
|
+
redirect(302, `/docs/${product}/${activeVersion}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Redirect to first doc in this product's version
|
|
23
|
+
redirect(302, `/docs/${product}/${version}/${docs[0].slug}`);
|
|
24
|
+
};
|
package/templates/minimal/src/routes/docs/[product=product]/[version]/[...slug]/+page.server.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractTableOfContents,
|
|
3
|
+
getAdjacentDocs,
|
|
4
|
+
isCategoryPage,
|
|
5
|
+
getCachedAllDocs,
|
|
6
|
+
getCachedDocBySlug,
|
|
7
|
+
getI18nConfig,
|
|
8
|
+
getProducts,
|
|
9
|
+
} from 'specra';
|
|
10
|
+
import type { PageServerLoad } from './$types';
|
|
11
|
+
|
|
12
|
+
export const load: PageServerLoad = async ({ params }) => {
|
|
13
|
+
const { product, version, slug: slugArray } = params;
|
|
14
|
+
const slug = slugArray.replace(/\/$/, '');
|
|
15
|
+
|
|
16
|
+
// Verify product exists
|
|
17
|
+
const products = getProducts();
|
|
18
|
+
const matchedProduct = products.find(p => p.slug === product);
|
|
19
|
+
if (!matchedProduct) {
|
|
20
|
+
return {
|
|
21
|
+
version,
|
|
22
|
+
slug,
|
|
23
|
+
product,
|
|
24
|
+
isCategory: false,
|
|
25
|
+
isNotFound: true,
|
|
26
|
+
doc: null,
|
|
27
|
+
categoryTitle: null,
|
|
28
|
+
categoryDescription: null,
|
|
29
|
+
categoryTabGroup: undefined,
|
|
30
|
+
toc: [],
|
|
31
|
+
previous: null,
|
|
32
|
+
next: null,
|
|
33
|
+
title: 'Page Not Found',
|
|
34
|
+
description: 'The requested documentation page could not be found.',
|
|
35
|
+
ogUrl: `/docs/${product}/${version}/${slug}`,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const i18nConfig = getI18nConfig();
|
|
40
|
+
const slugParts = slug.split('/');
|
|
41
|
+
let locale: string | undefined;
|
|
42
|
+
if (i18nConfig && i18nConfig.locales.includes(slugParts[0])) {
|
|
43
|
+
locale = slugParts[0];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const allDocs = await getCachedAllDocs(version, locale, product);
|
|
47
|
+
const isCategory = isCategoryPage(slug, allDocs);
|
|
48
|
+
const doc = await getCachedDocBySlug(slug, version, product);
|
|
49
|
+
const urlPrefix = `/docs/${product}/${version}`;
|
|
50
|
+
|
|
51
|
+
let title = 'Page Not Found';
|
|
52
|
+
let description = 'The requested documentation page could not be found.';
|
|
53
|
+
let ogUrl = `${urlPrefix}/${slug}`;
|
|
54
|
+
|
|
55
|
+
if (doc) {
|
|
56
|
+
title = doc.meta.title || doc.title;
|
|
57
|
+
description = doc.meta.description || `Documentation for ${title}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!doc && isCategory) {
|
|
61
|
+
const categoryDoc = allDocs.find((d) => d.slug.startsWith(slug + '/'));
|
|
62
|
+
const categoryTabGroup = categoryDoc?.meta?.tab_group || categoryDoc?.categoryTabGroup;
|
|
63
|
+
const categoryTitle = slug
|
|
64
|
+
.split('/')
|
|
65
|
+
.pop()
|
|
66
|
+
?.replace(/-/g, ' ')
|
|
67
|
+
.replace(/\b\w/g, (l) => l.toUpperCase()) || 'Category';
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
version,
|
|
71
|
+
slug,
|
|
72
|
+
product,
|
|
73
|
+
isCategory: true,
|
|
74
|
+
isNotFound: false,
|
|
75
|
+
doc: null,
|
|
76
|
+
categoryTitle,
|
|
77
|
+
categoryDescription: 'Browse the documentation in this section.',
|
|
78
|
+
categoryTabGroup,
|
|
79
|
+
toc: [],
|
|
80
|
+
previous: null,
|
|
81
|
+
next: null,
|
|
82
|
+
title,
|
|
83
|
+
description,
|
|
84
|
+
ogUrl,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!doc) {
|
|
89
|
+
return {
|
|
90
|
+
version,
|
|
91
|
+
slug,
|
|
92
|
+
product,
|
|
93
|
+
isCategory: false,
|
|
94
|
+
isNotFound: true,
|
|
95
|
+
doc: null,
|
|
96
|
+
categoryTitle: null,
|
|
97
|
+
categoryDescription: null,
|
|
98
|
+
categoryTabGroup: undefined,
|
|
99
|
+
toc: [],
|
|
100
|
+
previous: null,
|
|
101
|
+
next: null,
|
|
102
|
+
title,
|
|
103
|
+
description,
|
|
104
|
+
ogUrl,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const toc = extractTableOfContents(doc.meta.content || doc.content);
|
|
109
|
+
const { previous, next } = getAdjacentDocs(slug, allDocs);
|
|
110
|
+
const showCategoryIndex = isCategory && !!doc;
|
|
111
|
+
const matchingDoc = allDocs.find((d) => d.slug === slug);
|
|
112
|
+
const currentPageTabGroup = doc.meta?.tab_group || matchingDoc?.categoryTabGroup;
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
version,
|
|
116
|
+
slug,
|
|
117
|
+
product,
|
|
118
|
+
isCategory: showCategoryIndex,
|
|
119
|
+
isNotFound: false,
|
|
120
|
+
doc,
|
|
121
|
+
categoryTitle: null,
|
|
122
|
+
categoryDescription: null,
|
|
123
|
+
categoryTabGroup: currentPageTabGroup,
|
|
124
|
+
toc,
|
|
125
|
+
previous: previous ? { title: previous.meta.title, slug: previous.slug } : null,
|
|
126
|
+
next: next ? { title: next.meta.title, slug: next.slug } : null,
|
|
127
|
+
title,
|
|
128
|
+
description,
|
|
129
|
+
ogUrl,
|
|
130
|
+
};
|
|
131
|
+
};
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
TableOfContents,
|
|
4
|
+
Header,
|
|
5
|
+
TabGroups,
|
|
6
|
+
DocLayout,
|
|
7
|
+
CategoryIndex,
|
|
8
|
+
HotReloadIndicator,
|
|
9
|
+
DevModeBadge,
|
|
10
|
+
MdxHotReload,
|
|
11
|
+
MdxContent,
|
|
12
|
+
NotFoundContent,
|
|
13
|
+
SearchHighlight,
|
|
14
|
+
MobileDocLayout,
|
|
15
|
+
mdxComponents,
|
|
16
|
+
} from 'specra/components';
|
|
17
|
+
import type { PageData } from './$types';
|
|
18
|
+
|
|
19
|
+
let { data }: { data: PageData } = $props();
|
|
20
|
+
|
|
21
|
+
let allDocsCompat: any[] = $derived(data.allDocs);
|
|
22
|
+
let previousDoc = $derived(data.previous ?? undefined);
|
|
23
|
+
let nextDoc = $derived(data.next ?? undefined);
|
|
24
|
+
let categoryTitle = $derived(data.categoryTitle ?? undefined);
|
|
25
|
+
let categoryDescription = $derived(data.categoryDescription ?? undefined);
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<svelte:head>
|
|
29
|
+
<title>{data.title}</title>
|
|
30
|
+
<meta name="description" content={data.description} />
|
|
31
|
+
<meta property="og:title" content={data.title} />
|
|
32
|
+
<meta property="og:description" content={data.description} />
|
|
33
|
+
<meta property="og:url" content={data.ogUrl} />
|
|
34
|
+
<meta property="og:site_name" content="Documentation Platform" />
|
|
35
|
+
<meta property="og:type" content="article" />
|
|
36
|
+
<meta property="og:locale" content="en_US" />
|
|
37
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
38
|
+
<meta name="twitter:title" content={data.title} />
|
|
39
|
+
<meta name="twitter:description" content={data.description} />
|
|
40
|
+
<link rel="canonical" href={data.ogUrl} />
|
|
41
|
+
</svelte:head>
|
|
42
|
+
|
|
43
|
+
{#if !data.doc && data.isCategory}
|
|
44
|
+
<!-- Category page without doc content -->
|
|
45
|
+
<MobileDocLayout
|
|
46
|
+
docs={allDocsCompat}
|
|
47
|
+
version={data.version}
|
|
48
|
+
product={data.product}
|
|
49
|
+
config={data.config}
|
|
50
|
+
activeTabGroup={data.categoryTabGroup}
|
|
51
|
+
>
|
|
52
|
+
{#snippet header()}
|
|
53
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config} products={data.products} currentProduct={data.product}>
|
|
54
|
+
{#snippet subheader()}
|
|
55
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
56
|
+
<TabGroups
|
|
57
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
58
|
+
activeTabId={data.categoryTabGroup}
|
|
59
|
+
docs={allDocsCompat}
|
|
60
|
+
version={data.version}
|
|
61
|
+
product={data.product}
|
|
62
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
63
|
+
/>
|
|
64
|
+
{/if}
|
|
65
|
+
{/snippet}
|
|
66
|
+
</Header>
|
|
67
|
+
{/snippet}
|
|
68
|
+
<CategoryIndex
|
|
69
|
+
categoryPath={data.slug}
|
|
70
|
+
version={data.version}
|
|
71
|
+
product={data.product}
|
|
72
|
+
allDocs={allDocsCompat}
|
|
73
|
+
title={categoryTitle}
|
|
74
|
+
description={categoryDescription}
|
|
75
|
+
config={data.config}
|
|
76
|
+
/>
|
|
77
|
+
</MobileDocLayout>
|
|
78
|
+
<MdxHotReload />
|
|
79
|
+
<HotReloadIndicator />
|
|
80
|
+
<DevModeBadge />
|
|
81
|
+
{:else if data.isNotFound}
|
|
82
|
+
<!-- Not found -->
|
|
83
|
+
<MobileDocLayout
|
|
84
|
+
docs={allDocsCompat}
|
|
85
|
+
version={data.version}
|
|
86
|
+
product={data.product}
|
|
87
|
+
config={data.config}
|
|
88
|
+
>
|
|
89
|
+
{#snippet header()}
|
|
90
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config} products={data.products} currentProduct={data.product}>
|
|
91
|
+
{#snippet subheader()}
|
|
92
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
93
|
+
<TabGroups
|
|
94
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
95
|
+
activeTabId={data.categoryTabGroup}
|
|
96
|
+
docs={allDocsCompat}
|
|
97
|
+
version={data.version}
|
|
98
|
+
product={data.product}
|
|
99
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
100
|
+
/>
|
|
101
|
+
{/if}
|
|
102
|
+
{/snippet}
|
|
103
|
+
</Header>
|
|
104
|
+
{/snippet}
|
|
105
|
+
<NotFoundContent version={data.version} />
|
|
106
|
+
</MobileDocLayout>
|
|
107
|
+
<MdxHotReload />
|
|
108
|
+
<HotReloadIndicator />
|
|
109
|
+
<DevModeBadge />
|
|
110
|
+
{:else if data.doc}
|
|
111
|
+
<!-- Normal doc or category with doc content -->
|
|
112
|
+
<MobileDocLayout
|
|
113
|
+
docs={allDocsCompat}
|
|
114
|
+
version={data.version}
|
|
115
|
+
product={data.product}
|
|
116
|
+
config={data.config}
|
|
117
|
+
activeTabGroup={data.categoryTabGroup}
|
|
118
|
+
>
|
|
119
|
+
{#snippet header()}
|
|
120
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config} products={data.products} currentProduct={data.product}>
|
|
121
|
+
{#snippet subheader()}
|
|
122
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
123
|
+
<TabGroups
|
|
124
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
125
|
+
activeTabId={data.categoryTabGroup}
|
|
126
|
+
docs={allDocsCompat}
|
|
127
|
+
version={data.version}
|
|
128
|
+
product={data.product}
|
|
129
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
130
|
+
/>
|
|
131
|
+
{/if}
|
|
132
|
+
{/snippet}
|
|
133
|
+
</Header>
|
|
134
|
+
{/snippet}
|
|
135
|
+
{#snippet toc()}
|
|
136
|
+
{#if !data.isCategory}
|
|
137
|
+
<TableOfContents items={data.toc} config={data.config} />
|
|
138
|
+
{/if}
|
|
139
|
+
{/snippet}
|
|
140
|
+
|
|
141
|
+
{#if data.isCategory}
|
|
142
|
+
{#snippet categoryContent()}
|
|
143
|
+
{#if data.doc?.contentNodes}
|
|
144
|
+
<MdxContent nodes={data.doc.contentNodes} components={mdxComponents} />
|
|
145
|
+
{:else if data.doc?.content}
|
|
146
|
+
{@html data.doc.content}
|
|
147
|
+
{/if}
|
|
148
|
+
{/snippet}
|
|
149
|
+
<CategoryIndex
|
|
150
|
+
categoryPath={data.slug}
|
|
151
|
+
version={data.version}
|
|
152
|
+
allDocs={allDocsCompat}
|
|
153
|
+
title={data.doc.meta.title}
|
|
154
|
+
description={data.doc.meta.description}
|
|
155
|
+
content={categoryContent}
|
|
156
|
+
config={data.config}
|
|
157
|
+
/>
|
|
158
|
+
{:else}
|
|
159
|
+
<SearchHighlight />
|
|
160
|
+
<DocLayout
|
|
161
|
+
meta={data.doc.meta}
|
|
162
|
+
previousDoc={previousDoc}
|
|
163
|
+
nextDoc={nextDoc}
|
|
164
|
+
version={data.version}
|
|
165
|
+
slug={data.slug}
|
|
166
|
+
product={data.product}
|
|
167
|
+
config={data.config}
|
|
168
|
+
>
|
|
169
|
+
{#if data.doc.contentNodes}
|
|
170
|
+
<MdxContent nodes={data.doc.contentNodes} components={mdxComponents} />
|
|
171
|
+
{:else}
|
|
172
|
+
{@html data.doc.content}
|
|
173
|
+
{/if}
|
|
174
|
+
</DocLayout>
|
|
175
|
+
{/if}
|
|
176
|
+
</MobileDocLayout>
|
|
177
|
+
<MdxHotReload />
|
|
178
|
+
<HotReloadIndicator />
|
|
179
|
+
<DevModeBadge />
|
|
180
|
+
{/if}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { getCachedVersions, getCachedAllDocs, getEffectiveConfig, getI18nConfig, getVersionsMeta, getProducts, loadVersionConfig } from 'specra';
|
|
2
|
+
import { redirect } from '@sveltejs/kit';
|
|
3
|
+
import type { LayoutServerLoad } from './$types';
|
|
4
|
+
|
|
5
|
+
export const load: LayoutServerLoad = async ({ params }) => {
|
|
6
|
+
const { version } = params;
|
|
7
|
+
|
|
8
|
+
// Route disambiguation: if this "version" is actually a product slug,
|
|
9
|
+
// the +page.server.ts will handle the redirect. The layout still needs
|
|
10
|
+
// to return data for the version case.
|
|
11
|
+
const products = getProducts();
|
|
12
|
+
const isProduct = products.some(p => p.slug === version);
|
|
13
|
+
if (isProduct) {
|
|
14
|
+
// Return minimal data — the page will redirect before rendering
|
|
15
|
+
return { allDocs: [], versions: [], versionsMeta: [], config: getEffectiveConfig(''), products };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const i18nConfig = getI18nConfig();
|
|
19
|
+
const defaultLocale = i18nConfig?.defaultLocale || 'en';
|
|
20
|
+
|
|
21
|
+
// Block access to hidden versions — redirect to active version
|
|
22
|
+
const currentVersionConfig = loadVersionConfig(version);
|
|
23
|
+
if (currentVersionConfig?.hidden) {
|
|
24
|
+
const config = getEffectiveConfig(version);
|
|
25
|
+
const activeVersion = config.site?.activeVersion || 'v1.0.0';
|
|
26
|
+
throw redirect(302, `/docs/${activeVersion}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const allDocs = await getCachedAllDocs(version, defaultLocale);
|
|
30
|
+
const versions = getCachedVersions();
|
|
31
|
+
const config = getEffectiveConfig(version);
|
|
32
|
+
const versionsMeta = getVersionsMeta(versions);
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
allDocs,
|
|
36
|
+
versions,
|
|
37
|
+
versionsMeta,
|
|
38
|
+
config,
|
|
39
|
+
products,
|
|
40
|
+
versionBanner: currentVersionConfig?.banner,
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { redirect } from '@sveltejs/kit';
|
|
2
|
+
import { getCachedVersions, getCachedAllDocs, getProducts, getEffectiveConfig } from 'specra';
|
|
3
|
+
import type { PageServerLoad } from './$types';
|
|
4
|
+
|
|
5
|
+
export const load: PageServerLoad = async ({ params }) => {
|
|
6
|
+
const { version } = params;
|
|
7
|
+
|
|
8
|
+
// Route disambiguation: check if this "version" is actually a product slug
|
|
9
|
+
const products = getProducts();
|
|
10
|
+
const matchedProduct = products.find(p => p.slug === version);
|
|
11
|
+
if (matchedProduct) {
|
|
12
|
+
// This is /docs/{product} — redirect to the product's active version
|
|
13
|
+
const config = getEffectiveConfig('', version);
|
|
14
|
+
const activeVersion = matchedProduct.config.activeVersion || config.site?.activeVersion || 'v1.0.0';
|
|
15
|
+
redirect(302, `/docs/${version}/${activeVersion}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Standard version route
|
|
19
|
+
const docs = await getCachedAllDocs(version);
|
|
20
|
+
|
|
21
|
+
if (docs.length === 0) {
|
|
22
|
+
redirect(302, '/docs/v1.0.0');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Redirect to first doc
|
|
26
|
+
redirect(302, `/docs/${version}/${docs[0].slug}`);
|
|
27
|
+
};
|