create-specra 0.2.3 → 0.2.5
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/README.md +84 -8
- package/package.json +1 -1
- package/templates/book-docs/package.json +1 -1
- package/templates/book-docs/src/app.css +80 -1
- package/templates/book-docs/src/routes/+layout.server.ts +1 -1
- package/templates/book-docs/src/routes/docs/[version]/+layout.server.ts +31 -0
- package/templates/book-docs/src/routes/docs/[version]/[...slug]/+page.server.ts +8 -13
- package/templates/book-docs/src/routes/docs/[version]/[...slug]/+page.svelte +40 -3
- package/templates/jbrains-docs/package.json +1 -1
- package/templates/jbrains-docs/src/app.css +80 -1
- package/templates/jbrains-docs/src/routes/docs/[version]/+layout.server.ts +31 -0
- package/templates/jbrains-docs/src/routes/docs/[version]/[...slug]/+page.server.ts +8 -13
- package/templates/jbrains-docs/src/routes/docs/[version]/[...slug]/+page.svelte +40 -3
- package/templates/minimal/package.json +1 -1
- package/templates/minimal/src/app.css +80 -1
- package/templates/minimal/src/routes/+layout.server.ts +1 -1
- package/templates/minimal/src/routes/docs/[version]/+layout.server.ts +31 -0
- package/templates/minimal/src/routes/docs/[version]/[...slug]/+page.server.ts +8 -13
- package/templates/minimal/src/routes/docs/[version]/[...slug]/+page.svelte +40 -3
package/README.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
The fastest way to create a new Specra documentation site. Scaffold a complete documentation project with a single command.
|
|
4
4
|
|
|
5
|
+
## What is Specra?
|
|
6
|
+
|
|
7
|
+
Specra is a modern documentation library for SvelteKit that provides:
|
|
8
|
+
- Multi-version documentation support
|
|
9
|
+
- API reference generation
|
|
10
|
+
- Full-text search
|
|
11
|
+
- MDX-powered content
|
|
12
|
+
- Beautiful UI components
|
|
13
|
+
|
|
14
|
+
The official Specra site ([specra-docs](https://specra-docs.com)) also offers a SaaS platform with paid tiers (Starter, Pro, Enterprise) including authentication, Stripe/M-Pesa billing, and a user dashboard. The CLI scaffolds free, self-hosted documentation sites — no billing features are included in generated projects.
|
|
15
|
+
|
|
5
16
|
## Usage
|
|
6
17
|
|
|
7
18
|
### With npx (recommended)
|
|
@@ -142,16 +153,81 @@ Once your project is created, you can:
|
|
|
142
153
|
|
|
143
154
|
4. Customize your site in `specra.config.json`
|
|
144
155
|
|
|
145
|
-
##
|
|
156
|
+
## Deployment
|
|
146
157
|
|
|
147
|
-
Specra
|
|
148
|
-
- Multi-version documentation support
|
|
149
|
-
- API reference generation
|
|
150
|
-
- Full-text search
|
|
151
|
-
- MDX-powered content
|
|
152
|
-
- Beautiful UI components
|
|
158
|
+
Specra projects are standard SvelteKit apps, so you can deploy anywhere SvelteKit runs.
|
|
153
159
|
|
|
154
|
-
|
|
160
|
+
### Static Hosting (Vercel, Netlify, Cloudflare Pages)
|
|
161
|
+
|
|
162
|
+
Install the appropriate SvelteKit adapter:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# Vercel
|
|
166
|
+
npm install -D @sveltejs/adapter-vercel
|
|
167
|
+
|
|
168
|
+
# Netlify
|
|
169
|
+
npm install -D @sveltejs/adapter-netlify
|
|
170
|
+
|
|
171
|
+
# Cloudflare Pages
|
|
172
|
+
npm install -D @sveltejs/adapter-cloudflare
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Update `svelte.config.js` to use the adapter:
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
import adapter from '@sveltejs/adapter-vercel'; // or adapter-netlify, etc.
|
|
179
|
+
|
|
180
|
+
export default {
|
|
181
|
+
kit: {
|
|
182
|
+
adapter: adapter()
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Then push to your Git provider — the platform handles the rest.
|
|
188
|
+
|
|
189
|
+
### Node Server (VPS, Docker, Railway)
|
|
190
|
+
|
|
191
|
+
Use `@sveltejs/adapter-node` (included by default):
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
npm run build
|
|
195
|
+
node build
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The server listens on port 3000 by default. Configure with environment variables:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
PORT=8080 HOST=0.0.0.0 node build
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Static Site Generation
|
|
205
|
+
|
|
206
|
+
For fully static docs with no server needed:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
npm install -D @sveltejs/adapter-static
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Update `svelte.config.js`:
|
|
213
|
+
|
|
214
|
+
```js
|
|
215
|
+
import adapter from '@sveltejs/adapter-static';
|
|
216
|
+
|
|
217
|
+
export default {
|
|
218
|
+
kit: {
|
|
219
|
+
adapter: adapter({
|
|
220
|
+
fallback: '404.html'
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
npm run build
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Upload the `build/` directory to any static host (GitHub Pages, S3, etc.).
|
|
155
231
|
|
|
156
232
|
## Learn More
|
|
157
233
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,86 @@
|
|
|
1
1
|
@import "specra/styles";
|
|
2
2
|
@source "./**/*.{js,ts,svelte}";
|
|
3
3
|
|
|
4
|
-
@theme {
|
|
4
|
+
@theme inline {
|
|
5
5
|
--font-sans: "Geist", "Geist Fallback", system-ui, sans-serif;
|
|
6
6
|
--font-mono: "Geist Mono", "Geist Mono Fallback", monospace;
|
|
7
7
|
}
|
|
8
|
+
|
|
9
|
+
/* =============================================
|
|
10
|
+
THEME OVERRIDES
|
|
11
|
+
Uncomment a :root block for light theme and/or
|
|
12
|
+
a .dark block for dark theme. Mix and match!
|
|
13
|
+
============================================= */
|
|
14
|
+
|
|
15
|
+
/* ─── Mint Light ─── */
|
|
16
|
+
/* :root {
|
|
17
|
+
--primary: oklch(0.45 0.18 162);
|
|
18
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
19
|
+
--ring: oklch(0.45 0.18 162);
|
|
20
|
+
} */
|
|
21
|
+
|
|
22
|
+
/* ─── Ocean Light ─── */
|
|
23
|
+
/* :root {
|
|
24
|
+
--primary: oklch(0.45 0.18 240);
|
|
25
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
26
|
+
--ring: oklch(0.45 0.18 240);
|
|
27
|
+
} */
|
|
28
|
+
|
|
29
|
+
/* ─── Rose Light ─── */
|
|
30
|
+
/* :root {
|
|
31
|
+
--primary: oklch(0.50 0.20 350);
|
|
32
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
33
|
+
--ring: oklch(0.50 0.20 350);
|
|
34
|
+
} */
|
|
35
|
+
|
|
36
|
+
/* ─── Amber Light ─── */
|
|
37
|
+
/* :root {
|
|
38
|
+
--primary: oklch(0.55 0.16 70);
|
|
39
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
40
|
+
--ring: oklch(0.55 0.16 70);
|
|
41
|
+
} */
|
|
42
|
+
|
|
43
|
+
/* ─── Violet Light ─── */
|
|
44
|
+
/* :root {
|
|
45
|
+
--primary: oklch(0.48 0.22 290);
|
|
46
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
47
|
+
--ring: oklch(0.48 0.22 290);
|
|
48
|
+
} */
|
|
49
|
+
|
|
50
|
+
/* ─── Mint Dark ─── */
|
|
51
|
+
/* .dark {
|
|
52
|
+
--primary: oklch(0.72 0.18 162);
|
|
53
|
+
--primary-foreground: oklch(0.10 0.03 160);
|
|
54
|
+
--ring: oklch(0.72 0.18 162);
|
|
55
|
+
} */
|
|
56
|
+
|
|
57
|
+
/* ─── Ocean Dark ─── */
|
|
58
|
+
/* .dark {
|
|
59
|
+
--primary: oklch(0.68 0.16 240);
|
|
60
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
61
|
+
--ring: oklch(0.68 0.16 240);
|
|
62
|
+
} */
|
|
63
|
+
|
|
64
|
+
/* ─── Rose Dark ─── */
|
|
65
|
+
/* .dark {
|
|
66
|
+
--primary: oklch(0.70 0.18 350);
|
|
67
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
68
|
+
--ring: oklch(0.70 0.18 350);
|
|
69
|
+
} */
|
|
70
|
+
|
|
71
|
+
/* ─── Amber Dark ─── */
|
|
72
|
+
/* .dark {
|
|
73
|
+
--primary: oklch(0.75 0.16 70);
|
|
74
|
+
--primary-foreground: oklch(0.10 0.03 70);
|
|
75
|
+
--ring: oklch(0.75 0.16 70);
|
|
76
|
+
} */
|
|
77
|
+
|
|
78
|
+
/* ─── Violet Dark ─── */
|
|
79
|
+
/* .dark {
|
|
80
|
+
--primary: oklch(0.70 0.20 290);
|
|
81
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
82
|
+
--ring: oklch(0.70 0.20 290);
|
|
83
|
+
} */
|
|
84
|
+
|
|
85
|
+
/* For full theme customization (backgrounds, borders, sidebar, etc.)
|
|
86
|
+
see: https://specra-docs.com/docs/v1.0.0/configuration/theming */
|
|
@@ -6,7 +6,7 @@ import type { SpecraConfig } from 'specra';
|
|
|
6
6
|
initConfig(specraConfig as unknown as Partial<SpecraConfig>);
|
|
7
7
|
|
|
8
8
|
export const prerender = true;
|
|
9
|
-
export const trailingSlash = '
|
|
9
|
+
export const trailingSlash = 'never';
|
|
10
10
|
|
|
11
11
|
export const load: LayoutServerLoad = async () => {
|
|
12
12
|
const config = getConfig();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getCachedVersions, getCachedAllDocs, getEffectiveConfig, getI18nConfig, getVersionsMeta, 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
|
+
const i18nConfig = getI18nConfig();
|
|
9
|
+
const defaultLocale = i18nConfig?.defaultLocale || 'en';
|
|
10
|
+
|
|
11
|
+
// Block access to hidden versions — redirect to active version
|
|
12
|
+
const currentVersionConfig = loadVersionConfig(version);
|
|
13
|
+
if (currentVersionConfig?.hidden) {
|
|
14
|
+
const config = getEffectiveConfig(version);
|
|
15
|
+
const activeVersion = config.site?.activeVersion || 'v1.0.0';
|
|
16
|
+
throw redirect(302, `/docs/${activeVersion}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const allDocs = await getCachedAllDocs(version, defaultLocale);
|
|
20
|
+
const versions = getCachedVersions();
|
|
21
|
+
const config = getEffectiveConfig(version);
|
|
22
|
+
const versionsMeta = getVersionsMeta(versions);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
allDocs,
|
|
26
|
+
versions,
|
|
27
|
+
versionsMeta,
|
|
28
|
+
config,
|
|
29
|
+
versionBanner: currentVersionConfig?.banner,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -2,28 +2,17 @@ import {
|
|
|
2
2
|
extractTableOfContents,
|
|
3
3
|
getAdjacentDocs,
|
|
4
4
|
isCategoryPage,
|
|
5
|
-
getCachedVersions,
|
|
6
5
|
getCachedAllDocs,
|
|
7
6
|
getCachedDocBySlug,
|
|
8
7
|
getI18nConfig,
|
|
9
|
-
getConfig,
|
|
10
8
|
} from 'specra';
|
|
11
9
|
import type { PageServerLoad } from './$types';
|
|
12
10
|
|
|
13
|
-
export const load: PageServerLoad = async ({ params }) => {
|
|
11
|
+
export const load: PageServerLoad = async ({ params, parent }) => {
|
|
14
12
|
const { version, slug: slugArray } = params;
|
|
15
13
|
const slug = slugArray.replace(/\/$/, '');
|
|
14
|
+
const { allDocs, versions, config, versionsMeta, versionBanner } = await parent();
|
|
16
15
|
|
|
17
|
-
const i18nConfig = getI18nConfig();
|
|
18
|
-
const slugParts = slug.split('/');
|
|
19
|
-
let locale: string | undefined;
|
|
20
|
-
if (i18nConfig && i18nConfig.locales.includes(slugParts[0])) {
|
|
21
|
-
locale = slugParts[0];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const allDocs = await getCachedAllDocs(version, locale);
|
|
25
|
-
const versions = getCachedVersions();
|
|
26
|
-
const config = getConfig();
|
|
27
16
|
const isCategory = isCategoryPage(slug, allDocs);
|
|
28
17
|
const doc = await getCachedDocBySlug(slug, version);
|
|
29
18
|
|
|
@@ -51,6 +40,8 @@ export const load: PageServerLoad = async ({ params }) => {
|
|
|
51
40
|
slug,
|
|
52
41
|
allDocs,
|
|
53
42
|
versions,
|
|
43
|
+
versionsMeta,
|
|
44
|
+
versionBanner,
|
|
54
45
|
config,
|
|
55
46
|
isCategory: true,
|
|
56
47
|
isNotFound: false,
|
|
@@ -74,6 +65,8 @@ export const load: PageServerLoad = async ({ params }) => {
|
|
|
74
65
|
slug,
|
|
75
66
|
allDocs,
|
|
76
67
|
versions,
|
|
68
|
+
versionsMeta,
|
|
69
|
+
versionBanner,
|
|
77
70
|
config,
|
|
78
71
|
isCategory: false,
|
|
79
72
|
isNotFound: true,
|
|
@@ -102,6 +95,8 @@ export const load: PageServerLoad = async ({ params }) => {
|
|
|
102
95
|
slug,
|
|
103
96
|
allDocs,
|
|
104
97
|
versions,
|
|
98
|
+
versionsMeta,
|
|
99
|
+
versionBanner,
|
|
105
100
|
config,
|
|
106
101
|
isCategory: showCategoryIndex,
|
|
107
102
|
isNotFound: false,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
TableOfContents,
|
|
4
4
|
Header,
|
|
5
|
+
TabGroups,
|
|
5
6
|
DocLayout,
|
|
6
7
|
CategoryIndex,
|
|
7
8
|
HotReloadIndicator,
|
|
@@ -44,7 +45,19 @@
|
|
|
44
45
|
activeTabGroup={data.categoryTabGroup}
|
|
45
46
|
>
|
|
46
47
|
{#snippet header()}
|
|
47
|
-
<Header currentVersion={data.version} versions={data.versions} config={data.config}
|
|
48
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config}>
|
|
49
|
+
{#snippet subheader()}
|
|
50
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
51
|
+
<TabGroups
|
|
52
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
53
|
+
activeTabId={data.categoryTabGroup}
|
|
54
|
+
docs={allDocsCompat}
|
|
55
|
+
version={data.version}
|
|
56
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
57
|
+
/>
|
|
58
|
+
{/if}
|
|
59
|
+
{/snippet}
|
|
60
|
+
</Header>
|
|
48
61
|
{/snippet}
|
|
49
62
|
<CategoryIndex
|
|
50
63
|
categoryPath={data.slug}
|
|
@@ -65,7 +78,19 @@
|
|
|
65
78
|
config={data.config}
|
|
66
79
|
>
|
|
67
80
|
{#snippet header()}
|
|
68
|
-
<Header currentVersion={data.version} versions={data.versions} config={data.config}
|
|
81
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config}>
|
|
82
|
+
{#snippet subheader()}
|
|
83
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
84
|
+
<TabGroups
|
|
85
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
86
|
+
activeTabId={data.categoryTabGroup}
|
|
87
|
+
docs={allDocsCompat}
|
|
88
|
+
version={data.version}
|
|
89
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
90
|
+
/>
|
|
91
|
+
{/if}
|
|
92
|
+
{/snippet}
|
|
93
|
+
</Header>
|
|
69
94
|
{/snippet}
|
|
70
95
|
<NotFoundContent version={data.version} />
|
|
71
96
|
</MobileDocLayout>
|
|
@@ -80,7 +105,19 @@
|
|
|
80
105
|
activeTabGroup={data.categoryTabGroup}
|
|
81
106
|
>
|
|
82
107
|
{#snippet header()}
|
|
83
|
-
<Header currentVersion={data.version} versions={data.versions} config={data.config}
|
|
108
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config}>
|
|
109
|
+
{#snippet subheader()}
|
|
110
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
111
|
+
<TabGroups
|
|
112
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
113
|
+
activeTabId={data.categoryTabGroup}
|
|
114
|
+
docs={allDocsCompat}
|
|
115
|
+
version={data.version}
|
|
116
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
117
|
+
/>
|
|
118
|
+
{/if}
|
|
119
|
+
{/snippet}
|
|
120
|
+
</Header>
|
|
84
121
|
{/snippet}
|
|
85
122
|
{#snippet toc()}
|
|
86
123
|
{#if !data.isCategory}
|
|
@@ -1,7 +1,86 @@
|
|
|
1
1
|
@import "specra/styles";
|
|
2
2
|
@source "./**/*.{js,ts,svelte}";
|
|
3
3
|
|
|
4
|
-
@theme {
|
|
4
|
+
@theme inline {
|
|
5
5
|
--font-sans: "Geist", "Geist Fallback", system-ui, sans-serif;
|
|
6
6
|
--font-mono: "Geist Mono", "Geist Mono Fallback", monospace;
|
|
7
7
|
}
|
|
8
|
+
|
|
9
|
+
/* =============================================
|
|
10
|
+
THEME OVERRIDES
|
|
11
|
+
Uncomment a :root block for light theme and/or
|
|
12
|
+
a .dark block for dark theme. Mix and match!
|
|
13
|
+
============================================= */
|
|
14
|
+
|
|
15
|
+
/* ─── Mint Light ─── */
|
|
16
|
+
/* :root {
|
|
17
|
+
--primary: oklch(0.45 0.18 162);
|
|
18
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
19
|
+
--ring: oklch(0.45 0.18 162);
|
|
20
|
+
} */
|
|
21
|
+
|
|
22
|
+
/* ─── Ocean Light ─── */
|
|
23
|
+
/* :root {
|
|
24
|
+
--primary: oklch(0.45 0.18 240);
|
|
25
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
26
|
+
--ring: oklch(0.45 0.18 240);
|
|
27
|
+
} */
|
|
28
|
+
|
|
29
|
+
/* ─── Rose Light ─── */
|
|
30
|
+
/* :root {
|
|
31
|
+
--primary: oklch(0.50 0.20 350);
|
|
32
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
33
|
+
--ring: oklch(0.50 0.20 350);
|
|
34
|
+
} */
|
|
35
|
+
|
|
36
|
+
/* ─── Amber Light ─── */
|
|
37
|
+
/* :root {
|
|
38
|
+
--primary: oklch(0.55 0.16 70);
|
|
39
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
40
|
+
--ring: oklch(0.55 0.16 70);
|
|
41
|
+
} */
|
|
42
|
+
|
|
43
|
+
/* ─── Violet Light ─── */
|
|
44
|
+
/* :root {
|
|
45
|
+
--primary: oklch(0.48 0.22 290);
|
|
46
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
47
|
+
--ring: oklch(0.48 0.22 290);
|
|
48
|
+
} */
|
|
49
|
+
|
|
50
|
+
/* ─── Mint Dark ─── */
|
|
51
|
+
/* .dark {
|
|
52
|
+
--primary: oklch(0.72 0.18 162);
|
|
53
|
+
--primary-foreground: oklch(0.10 0.03 160);
|
|
54
|
+
--ring: oklch(0.72 0.18 162);
|
|
55
|
+
} */
|
|
56
|
+
|
|
57
|
+
/* ─── Ocean Dark ─── */
|
|
58
|
+
/* .dark {
|
|
59
|
+
--primary: oklch(0.68 0.16 240);
|
|
60
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
61
|
+
--ring: oklch(0.68 0.16 240);
|
|
62
|
+
} */
|
|
63
|
+
|
|
64
|
+
/* ─── Rose Dark ─── */
|
|
65
|
+
/* .dark {
|
|
66
|
+
--primary: oklch(0.70 0.18 350);
|
|
67
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
68
|
+
--ring: oklch(0.70 0.18 350);
|
|
69
|
+
} */
|
|
70
|
+
|
|
71
|
+
/* ─── Amber Dark ─── */
|
|
72
|
+
/* .dark {
|
|
73
|
+
--primary: oklch(0.75 0.16 70);
|
|
74
|
+
--primary-foreground: oklch(0.10 0.03 70);
|
|
75
|
+
--ring: oklch(0.75 0.16 70);
|
|
76
|
+
} */
|
|
77
|
+
|
|
78
|
+
/* ─── Violet Dark ─── */
|
|
79
|
+
/* .dark {
|
|
80
|
+
--primary: oklch(0.70 0.20 290);
|
|
81
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
82
|
+
--ring: oklch(0.70 0.20 290);
|
|
83
|
+
} */
|
|
84
|
+
|
|
85
|
+
/* For full theme customization (backgrounds, borders, sidebar, etc.)
|
|
86
|
+
see: https://specra-docs.com/docs/v1.0.0/configuration/theming */
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getCachedVersions, getCachedAllDocs, getEffectiveConfig, getI18nConfig, getVersionsMeta, 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
|
+
const i18nConfig = getI18nConfig();
|
|
9
|
+
const defaultLocale = i18nConfig?.defaultLocale || 'en';
|
|
10
|
+
|
|
11
|
+
// Block access to hidden versions — redirect to active version
|
|
12
|
+
const currentVersionConfig = loadVersionConfig(version);
|
|
13
|
+
if (currentVersionConfig?.hidden) {
|
|
14
|
+
const config = getEffectiveConfig(version);
|
|
15
|
+
const activeVersion = config.site?.activeVersion || 'v1.0.0';
|
|
16
|
+
throw redirect(302, `/docs/${activeVersion}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const allDocs = await getCachedAllDocs(version, defaultLocale);
|
|
20
|
+
const versions = getCachedVersions();
|
|
21
|
+
const config = getEffectiveConfig(version);
|
|
22
|
+
const versionsMeta = getVersionsMeta(versions);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
allDocs,
|
|
26
|
+
versions,
|
|
27
|
+
versionsMeta,
|
|
28
|
+
config,
|
|
29
|
+
versionBanner: currentVersionConfig?.banner,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -2,28 +2,17 @@ import {
|
|
|
2
2
|
extractTableOfContents,
|
|
3
3
|
getAdjacentDocs,
|
|
4
4
|
isCategoryPage,
|
|
5
|
-
getCachedVersions,
|
|
6
5
|
getCachedAllDocs,
|
|
7
6
|
getCachedDocBySlug,
|
|
8
7
|
getI18nConfig,
|
|
9
|
-
getConfig,
|
|
10
8
|
} from 'specra';
|
|
11
9
|
import type { PageServerLoad } from './$types';
|
|
12
10
|
|
|
13
|
-
export const load: PageServerLoad = async ({ params }) => {
|
|
11
|
+
export const load: PageServerLoad = async ({ params, parent }) => {
|
|
14
12
|
const { version, slug: slugArray } = params;
|
|
15
13
|
const slug = slugArray.replace(/\/$/, '');
|
|
14
|
+
const { allDocs, versions, config, versionsMeta, versionBanner } = await parent();
|
|
16
15
|
|
|
17
|
-
const i18nConfig = getI18nConfig();
|
|
18
|
-
const slugParts = slug.split('/');
|
|
19
|
-
let locale: string | undefined;
|
|
20
|
-
if (i18nConfig && i18nConfig.locales.includes(slugParts[0])) {
|
|
21
|
-
locale = slugParts[0];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const allDocs = await getCachedAllDocs(version, locale);
|
|
25
|
-
const versions = getCachedVersions();
|
|
26
|
-
const config = getConfig();
|
|
27
16
|
const isCategory = isCategoryPage(slug, allDocs);
|
|
28
17
|
const doc = await getCachedDocBySlug(slug, version);
|
|
29
18
|
|
|
@@ -51,6 +40,8 @@ export const load: PageServerLoad = async ({ params }) => {
|
|
|
51
40
|
slug,
|
|
52
41
|
allDocs,
|
|
53
42
|
versions,
|
|
43
|
+
versionsMeta,
|
|
44
|
+
versionBanner,
|
|
54
45
|
config,
|
|
55
46
|
isCategory: true,
|
|
56
47
|
isNotFound: false,
|
|
@@ -74,6 +65,8 @@ export const load: PageServerLoad = async ({ params }) => {
|
|
|
74
65
|
slug,
|
|
75
66
|
allDocs,
|
|
76
67
|
versions,
|
|
68
|
+
versionsMeta,
|
|
69
|
+
versionBanner,
|
|
77
70
|
config,
|
|
78
71
|
isCategory: false,
|
|
79
72
|
isNotFound: true,
|
|
@@ -102,6 +95,8 @@ export const load: PageServerLoad = async ({ params }) => {
|
|
|
102
95
|
slug,
|
|
103
96
|
allDocs,
|
|
104
97
|
versions,
|
|
98
|
+
versionsMeta,
|
|
99
|
+
versionBanner,
|
|
105
100
|
config,
|
|
106
101
|
isCategory: showCategoryIndex,
|
|
107
102
|
isNotFound: false,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
TableOfContents,
|
|
4
4
|
Header,
|
|
5
|
+
TabGroups,
|
|
5
6
|
DocLayout,
|
|
6
7
|
CategoryIndex,
|
|
7
8
|
HotReloadIndicator,
|
|
@@ -44,7 +45,19 @@
|
|
|
44
45
|
activeTabGroup={data.categoryTabGroup}
|
|
45
46
|
>
|
|
46
47
|
{#snippet header()}
|
|
47
|
-
<Header currentVersion={data.version} versions={data.versions} config={data.config}
|
|
48
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config}>
|
|
49
|
+
{#snippet subheader()}
|
|
50
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
51
|
+
<TabGroups
|
|
52
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
53
|
+
activeTabId={data.categoryTabGroup}
|
|
54
|
+
docs={allDocsCompat}
|
|
55
|
+
version={data.version}
|
|
56
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
57
|
+
/>
|
|
58
|
+
{/if}
|
|
59
|
+
{/snippet}
|
|
60
|
+
</Header>
|
|
48
61
|
{/snippet}
|
|
49
62
|
<CategoryIndex
|
|
50
63
|
categoryPath={data.slug}
|
|
@@ -65,7 +78,19 @@
|
|
|
65
78
|
config={data.config}
|
|
66
79
|
>
|
|
67
80
|
{#snippet header()}
|
|
68
|
-
<Header currentVersion={data.version} versions={data.versions} config={data.config}
|
|
81
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config}>
|
|
82
|
+
{#snippet subheader()}
|
|
83
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
84
|
+
<TabGroups
|
|
85
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
86
|
+
activeTabId={data.categoryTabGroup}
|
|
87
|
+
docs={allDocsCompat}
|
|
88
|
+
version={data.version}
|
|
89
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
90
|
+
/>
|
|
91
|
+
{/if}
|
|
92
|
+
{/snippet}
|
|
93
|
+
</Header>
|
|
69
94
|
{/snippet}
|
|
70
95
|
<NotFoundContent version={data.version} />
|
|
71
96
|
</MobileDocLayout>
|
|
@@ -80,7 +105,19 @@
|
|
|
80
105
|
activeTabGroup={data.categoryTabGroup}
|
|
81
106
|
>
|
|
82
107
|
{#snippet header()}
|
|
83
|
-
<Header currentVersion={data.version} versions={data.versions} config={data.config}
|
|
108
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config}>
|
|
109
|
+
{#snippet subheader()}
|
|
110
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
111
|
+
<TabGroups
|
|
112
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
113
|
+
activeTabId={data.categoryTabGroup}
|
|
114
|
+
docs={allDocsCompat}
|
|
115
|
+
version={data.version}
|
|
116
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
117
|
+
/>
|
|
118
|
+
{/if}
|
|
119
|
+
{/snippet}
|
|
120
|
+
</Header>
|
|
84
121
|
{/snippet}
|
|
85
122
|
{#snippet toc()}
|
|
86
123
|
{#if !data.isCategory}
|
|
@@ -1,7 +1,86 @@
|
|
|
1
1
|
@import "specra/styles";
|
|
2
2
|
@source "./**/*.{js,ts,svelte}";
|
|
3
3
|
|
|
4
|
-
@theme {
|
|
4
|
+
@theme inline {
|
|
5
5
|
--font-sans: "Geist", "Geist Fallback", system-ui, sans-serif;
|
|
6
6
|
--font-mono: "Geist Mono", "Geist Mono Fallback", monospace;
|
|
7
7
|
}
|
|
8
|
+
|
|
9
|
+
/* =============================================
|
|
10
|
+
THEME OVERRIDES
|
|
11
|
+
Uncomment a :root block for light theme and/or
|
|
12
|
+
a .dark block for dark theme. Mix and match!
|
|
13
|
+
============================================= */
|
|
14
|
+
|
|
15
|
+
/* ─── Mint Light ─── */
|
|
16
|
+
/* :root {
|
|
17
|
+
--primary: oklch(0.45 0.18 162);
|
|
18
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
19
|
+
--ring: oklch(0.45 0.18 162);
|
|
20
|
+
} */
|
|
21
|
+
|
|
22
|
+
/* ─── Ocean Light ─── */
|
|
23
|
+
/* :root {
|
|
24
|
+
--primary: oklch(0.45 0.18 240);
|
|
25
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
26
|
+
--ring: oklch(0.45 0.18 240);
|
|
27
|
+
} */
|
|
28
|
+
|
|
29
|
+
/* ─── Rose Light ─── */
|
|
30
|
+
/* :root {
|
|
31
|
+
--primary: oklch(0.50 0.20 350);
|
|
32
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
33
|
+
--ring: oklch(0.50 0.20 350);
|
|
34
|
+
} */
|
|
35
|
+
|
|
36
|
+
/* ─── Amber Light ─── */
|
|
37
|
+
/* :root {
|
|
38
|
+
--primary: oklch(0.55 0.16 70);
|
|
39
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
40
|
+
--ring: oklch(0.55 0.16 70);
|
|
41
|
+
} */
|
|
42
|
+
|
|
43
|
+
/* ─── Violet Light ─── */
|
|
44
|
+
/* :root {
|
|
45
|
+
--primary: oklch(0.48 0.22 290);
|
|
46
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
47
|
+
--ring: oklch(0.48 0.22 290);
|
|
48
|
+
} */
|
|
49
|
+
|
|
50
|
+
/* ─── Mint Dark ─── */
|
|
51
|
+
/* .dark {
|
|
52
|
+
--primary: oklch(0.72 0.18 162);
|
|
53
|
+
--primary-foreground: oklch(0.10 0.03 160);
|
|
54
|
+
--ring: oklch(0.72 0.18 162);
|
|
55
|
+
} */
|
|
56
|
+
|
|
57
|
+
/* ─── Ocean Dark ─── */
|
|
58
|
+
/* .dark {
|
|
59
|
+
--primary: oklch(0.68 0.16 240);
|
|
60
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
61
|
+
--ring: oklch(0.68 0.16 240);
|
|
62
|
+
} */
|
|
63
|
+
|
|
64
|
+
/* ─── Rose Dark ─── */
|
|
65
|
+
/* .dark {
|
|
66
|
+
--primary: oklch(0.70 0.18 350);
|
|
67
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
68
|
+
--ring: oklch(0.70 0.18 350);
|
|
69
|
+
} */
|
|
70
|
+
|
|
71
|
+
/* ─── Amber Dark ─── */
|
|
72
|
+
/* .dark {
|
|
73
|
+
--primary: oklch(0.75 0.16 70);
|
|
74
|
+
--primary-foreground: oklch(0.10 0.03 70);
|
|
75
|
+
--ring: oklch(0.75 0.16 70);
|
|
76
|
+
} */
|
|
77
|
+
|
|
78
|
+
/* ─── Violet Dark ─── */
|
|
79
|
+
/* .dark {
|
|
80
|
+
--primary: oklch(0.70 0.20 290);
|
|
81
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
82
|
+
--ring: oklch(0.70 0.20 290);
|
|
83
|
+
} */
|
|
84
|
+
|
|
85
|
+
/* For full theme customization (backgrounds, borders, sidebar, etc.)
|
|
86
|
+
see: https://specra-docs.com/docs/v1.0.0/configuration/theming */
|
|
@@ -6,7 +6,7 @@ import type { SpecraConfig } from 'specra';
|
|
|
6
6
|
initConfig(specraConfig as unknown as Partial<SpecraConfig>);
|
|
7
7
|
|
|
8
8
|
export const prerender = true;
|
|
9
|
-
export const trailingSlash = '
|
|
9
|
+
export const trailingSlash = 'never';
|
|
10
10
|
|
|
11
11
|
export const load: LayoutServerLoad = async () => {
|
|
12
12
|
const config = getConfig();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getCachedVersions, getCachedAllDocs, getEffectiveConfig, getI18nConfig, getVersionsMeta, 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
|
+
const i18nConfig = getI18nConfig();
|
|
9
|
+
const defaultLocale = i18nConfig?.defaultLocale || 'en';
|
|
10
|
+
|
|
11
|
+
// Block access to hidden versions — redirect to active version
|
|
12
|
+
const currentVersionConfig = loadVersionConfig(version);
|
|
13
|
+
if (currentVersionConfig?.hidden) {
|
|
14
|
+
const config = getEffectiveConfig(version);
|
|
15
|
+
const activeVersion = config.site?.activeVersion || 'v1.0.0';
|
|
16
|
+
throw redirect(302, `/docs/${activeVersion}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const allDocs = await getCachedAllDocs(version, defaultLocale);
|
|
20
|
+
const versions = getCachedVersions();
|
|
21
|
+
const config = getEffectiveConfig(version);
|
|
22
|
+
const versionsMeta = getVersionsMeta(versions);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
allDocs,
|
|
26
|
+
versions,
|
|
27
|
+
versionsMeta,
|
|
28
|
+
config,
|
|
29
|
+
versionBanner: currentVersionConfig?.banner,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -2,28 +2,17 @@ import {
|
|
|
2
2
|
extractTableOfContents,
|
|
3
3
|
getAdjacentDocs,
|
|
4
4
|
isCategoryPage,
|
|
5
|
-
getCachedVersions,
|
|
6
5
|
getCachedAllDocs,
|
|
7
6
|
getCachedDocBySlug,
|
|
8
7
|
getI18nConfig,
|
|
9
|
-
getConfig,
|
|
10
8
|
} from 'specra';
|
|
11
9
|
import type { PageServerLoad } from './$types';
|
|
12
10
|
|
|
13
|
-
export const load: PageServerLoad = async ({ params }) => {
|
|
11
|
+
export const load: PageServerLoad = async ({ params, parent }) => {
|
|
14
12
|
const { version, slug: slugArray } = params;
|
|
15
13
|
const slug = slugArray.replace(/\/$/, '');
|
|
14
|
+
const { allDocs, versions, config, versionsMeta, versionBanner } = await parent();
|
|
16
15
|
|
|
17
|
-
const i18nConfig = getI18nConfig();
|
|
18
|
-
const slugParts = slug.split('/');
|
|
19
|
-
let locale: string | undefined;
|
|
20
|
-
if (i18nConfig && i18nConfig.locales.includes(slugParts[0])) {
|
|
21
|
-
locale = slugParts[0];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const allDocs = await getCachedAllDocs(version, locale);
|
|
25
|
-
const versions = getCachedVersions();
|
|
26
|
-
const config = getConfig();
|
|
27
16
|
const isCategory = isCategoryPage(slug, allDocs);
|
|
28
17
|
const doc = await getCachedDocBySlug(slug, version);
|
|
29
18
|
|
|
@@ -51,6 +40,8 @@ export const load: PageServerLoad = async ({ params }) => {
|
|
|
51
40
|
slug,
|
|
52
41
|
allDocs,
|
|
53
42
|
versions,
|
|
43
|
+
versionsMeta,
|
|
44
|
+
versionBanner,
|
|
54
45
|
config,
|
|
55
46
|
isCategory: true,
|
|
56
47
|
isNotFound: false,
|
|
@@ -74,6 +65,8 @@ export const load: PageServerLoad = async ({ params }) => {
|
|
|
74
65
|
slug,
|
|
75
66
|
allDocs,
|
|
76
67
|
versions,
|
|
68
|
+
versionsMeta,
|
|
69
|
+
versionBanner,
|
|
77
70
|
config,
|
|
78
71
|
isCategory: false,
|
|
79
72
|
isNotFound: true,
|
|
@@ -102,6 +95,8 @@ export const load: PageServerLoad = async ({ params }) => {
|
|
|
102
95
|
slug,
|
|
103
96
|
allDocs,
|
|
104
97
|
versions,
|
|
98
|
+
versionsMeta,
|
|
99
|
+
versionBanner,
|
|
105
100
|
config,
|
|
106
101
|
isCategory: showCategoryIndex,
|
|
107
102
|
isNotFound: false,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
TableOfContents,
|
|
4
4
|
Header,
|
|
5
|
+
TabGroups,
|
|
5
6
|
DocLayout,
|
|
6
7
|
CategoryIndex,
|
|
7
8
|
HotReloadIndicator,
|
|
@@ -44,7 +45,19 @@
|
|
|
44
45
|
activeTabGroup={data.categoryTabGroup}
|
|
45
46
|
>
|
|
46
47
|
{#snippet header()}
|
|
47
|
-
<Header currentVersion={data.version} versions={data.versions} config={data.config}
|
|
48
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config}>
|
|
49
|
+
{#snippet subheader()}
|
|
50
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
51
|
+
<TabGroups
|
|
52
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
53
|
+
activeTabId={data.categoryTabGroup}
|
|
54
|
+
docs={allDocsCompat}
|
|
55
|
+
version={data.version}
|
|
56
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
57
|
+
/>
|
|
58
|
+
{/if}
|
|
59
|
+
{/snippet}
|
|
60
|
+
</Header>
|
|
48
61
|
{/snippet}
|
|
49
62
|
<CategoryIndex
|
|
50
63
|
categoryPath={data.slug}
|
|
@@ -65,7 +78,19 @@
|
|
|
65
78
|
config={data.config}
|
|
66
79
|
>
|
|
67
80
|
{#snippet header()}
|
|
68
|
-
<Header currentVersion={data.version} versions={data.versions} config={data.config}
|
|
81
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config}>
|
|
82
|
+
{#snippet subheader()}
|
|
83
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
84
|
+
<TabGroups
|
|
85
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
86
|
+
activeTabId={data.categoryTabGroup}
|
|
87
|
+
docs={allDocsCompat}
|
|
88
|
+
version={data.version}
|
|
89
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
90
|
+
/>
|
|
91
|
+
{/if}
|
|
92
|
+
{/snippet}
|
|
93
|
+
</Header>
|
|
69
94
|
{/snippet}
|
|
70
95
|
<NotFoundContent version={data.version} />
|
|
71
96
|
</MobileDocLayout>
|
|
@@ -80,7 +105,19 @@
|
|
|
80
105
|
activeTabGroup={data.categoryTabGroup}
|
|
81
106
|
>
|
|
82
107
|
{#snippet header()}
|
|
83
|
-
<Header currentVersion={data.version} versions={data.versions} config={data.config}
|
|
108
|
+
<Header currentVersion={data.version} versions={data.versions} versionsMeta={data.versionsMeta} versionBanner={data.versionBanner} config={data.config}>
|
|
109
|
+
{#snippet subheader()}
|
|
110
|
+
{#if data.config.navigation?.tabGroups && data.config.navigation.tabGroups.length > 0}
|
|
111
|
+
<TabGroups
|
|
112
|
+
tabGroups={data.config.navigation.tabGroups}
|
|
113
|
+
activeTabId={data.categoryTabGroup}
|
|
114
|
+
docs={allDocsCompat}
|
|
115
|
+
version={data.version}
|
|
116
|
+
flush={data.config.navigation?.sidebarStyle === 'flush'}
|
|
117
|
+
/>
|
|
118
|
+
{/if}
|
|
119
|
+
{/snippet}
|
|
120
|
+
</Header>
|
|
84
121
|
{/snippet}
|
|
85
122
|
{#snippet toc()}
|
|
86
123
|
{#if !data.isCategory}
|