@karaoke-cms/module-docs 0.9.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 +69 -0
- package/package.json +35 -0
- package/src/css-contract.ts +20 -0
- package/src/index.ts +40 -0
- package/src/pages/doc.astro +54 -0
- package/src/pages/home.astro +30 -0
- package/src/pages/list.astro +30 -0
- package/src/schema.ts +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# @karaoke-cms/module-docs
|
|
2
|
+
|
|
3
|
+
Docs module for karaoke-cms — documentation pages with a left-sidebar navigation showing all published docs in alphabetical order.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @karaoke-cms/module-docs
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
// karaoke.config.ts
|
|
15
|
+
import { defineConfig } from '@karaoke-cms/astro';
|
|
16
|
+
import { loadEnv } from '@karaoke-cms/astro/env';
|
|
17
|
+
import { blog } from '@karaoke-cms/module-blog';
|
|
18
|
+
import { docs } from '@karaoke-cms/module-docs';
|
|
19
|
+
import { themeDefault } from '@karaoke-cms/theme-default';
|
|
20
|
+
|
|
21
|
+
const env = loadEnv(new URL('.', import.meta.url));
|
|
22
|
+
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
vault: env.KARAOKE_VAULT,
|
|
25
|
+
theme: themeDefault({
|
|
26
|
+
implements: [blog({ mount: '/blog' }), docs({ mount: '/docs' })],
|
|
27
|
+
}),
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Configuration
|
|
32
|
+
|
|
33
|
+
| Option | Type | Default | Description |
|
|
34
|
+
|--------|------|---------|-------------|
|
|
35
|
+
| `mount` | `string` | `'/docs'` | URL prefix for all docs routes |
|
|
36
|
+
| `enabled` | `boolean` | `true` | Set `false` to disable the module entirely |
|
|
37
|
+
|
|
38
|
+
## Routes
|
|
39
|
+
|
|
40
|
+
All routes are relative to `mount`:
|
|
41
|
+
|
|
42
|
+
| Pattern | Description |
|
|
43
|
+
|---------|-------------|
|
|
44
|
+
| `{mount}` | Docs home — lists all published doc titles |
|
|
45
|
+
| `{mount}/list` | Standalone full-list view |
|
|
46
|
+
| `{mount}/[slug]` | Individual doc page with left-sidebar navigation |
|
|
47
|
+
|
|
48
|
+
## Frontmatter
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
---
|
|
52
|
+
title: "Getting Started" # required
|
|
53
|
+
publish: true # required to appear on site
|
|
54
|
+
date: 2026-01-15 # optional — YYYY-MM-DD
|
|
55
|
+
author: "Name" # optional — string or array
|
|
56
|
+
featured_image: "img/hero.jpg" # optional — relative to vault
|
|
57
|
+
description: "..." # optional — OG tags, AI-enriched
|
|
58
|
+
tags: [setup, guide] # optional — AI-enriched
|
|
59
|
+
reading_time: 3 # optional — AI-enriched, minutes
|
|
60
|
+
related: [slug-a, slug-b] # optional — AI-enriched, slugs
|
|
61
|
+
comments: false # optional — per-doc Giscus override (off by default)
|
|
62
|
+
---
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## What's new in 0.9.5
|
|
66
|
+
|
|
67
|
+
- **`featured_image` support** added to the docs schema
|
|
68
|
+
- **`enabled` flag** — `docs({ mount: '/docs', enabled: false })` disables the module without removing it from config
|
|
69
|
+
- Fixed workspace symlink registration — `@karaoke-cms/module-docs` is now correctly wired in the website app
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@karaoke-cms/module-docs",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.9.5",
|
|
5
|
+
"description": "Docs module for karaoke-cms — documentation pages with sidebar navigation",
|
|
6
|
+
"main": "./src/index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts",
|
|
9
|
+
"./schema": "./src/schema.ts",
|
|
10
|
+
"./pages/home": "./src/pages/home.astro",
|
|
11
|
+
"./pages/list": "./src/pages/list.astro",
|
|
12
|
+
"./pages/doc": "./src/pages/doc.astro"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"src/"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"astro",
|
|
19
|
+
"cms",
|
|
20
|
+
"docs",
|
|
21
|
+
"documentation",
|
|
22
|
+
"karaoke-cms"
|
|
23
|
+
],
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"astro": ">=6.0.0",
|
|
26
|
+
"@karaoke-cms/astro": "^0.9.5"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@karaoke-cms/astro": "workspace:*",
|
|
30
|
+
"astro": "^6.0.8"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"test": "echo \"Stub — no tests\""
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS class names the docs module promises to use in its markup.
|
|
3
|
+
* Themes implement these classes to style the docs section.
|
|
4
|
+
*/
|
|
5
|
+
export const cssContract = [
|
|
6
|
+
'docs-home',
|
|
7
|
+
'docs-home-list',
|
|
8
|
+
'docs-home-item',
|
|
9
|
+
'docs-list',
|
|
10
|
+
'docs-list-item',
|
|
11
|
+
'docs-article',
|
|
12
|
+
'docs-article-image',
|
|
13
|
+
'docs-article-title',
|
|
14
|
+
'docs-article-meta',
|
|
15
|
+
'docs-tag',
|
|
16
|
+
'docs-tag-list',
|
|
17
|
+
'docs-sidebar',
|
|
18
|
+
'docs-sidebar-list',
|
|
19
|
+
'docs-sidebar-item',
|
|
20
|
+
] as const;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { defineModule } from '@karaoke-cms/astro';
|
|
2
|
+
import { cssContract } from './css-contract.js';
|
|
3
|
+
|
|
4
|
+
export { cssContract } from './css-contract.js';
|
|
5
|
+
// docsSchema is exported from '@karaoke-cms/module-docs/schema' to avoid
|
|
6
|
+
// pulling zod into the config-loading context.
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Docs module — documentation pages with sidebar navigation.
|
|
10
|
+
*
|
|
11
|
+
* Publishes three routes:
|
|
12
|
+
* - `{mount}` — home page listing all doc titles
|
|
13
|
+
* - `{mount}/list` — standalone full docs list
|
|
14
|
+
* - `{mount}/[slug]` — individual doc with left-sidebar nav
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // karaoke.config.ts
|
|
18
|
+
* import { docs } from '@karaoke-cms/module-docs';
|
|
19
|
+
* export default defineConfig({
|
|
20
|
+
* modules: [docs({ mount: '/docs' })],
|
|
21
|
+
* theme: themeDefault({ implements: [docs({ mount: '/docs' })] }),
|
|
22
|
+
* });
|
|
23
|
+
*/
|
|
24
|
+
export const docs = defineModule({
|
|
25
|
+
id: 'docs',
|
|
26
|
+
cssContract,
|
|
27
|
+
|
|
28
|
+
// No default CSS — requires a theme implementation.
|
|
29
|
+
defaultCss: undefined,
|
|
30
|
+
|
|
31
|
+
routes: (mount) => [
|
|
32
|
+
{ pattern: mount || '/docs', entrypoint: '@karaoke-cms/module-docs/pages/home' },
|
|
33
|
+
{ pattern: `${mount}/list`, entrypoint: '@karaoke-cms/module-docs/pages/list' },
|
|
34
|
+
{ pattern: `${mount}/[slug]`, entrypoint: '@karaoke-cms/module-docs/pages/doc' },
|
|
35
|
+
],
|
|
36
|
+
|
|
37
|
+
menuEntries: (mount, id) => [
|
|
38
|
+
{ id, name: 'Docs', path: mount, section: 'main', weight: 20 },
|
|
39
|
+
],
|
|
40
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
// TODO: use mount path from virtual module once mount-aware routing is implemented.
|
|
3
|
+
// For now, mount is assumed to be /docs.
|
|
4
|
+
import { getCollection, render } from 'astro:content';
|
|
5
|
+
import DefaultPage from '@karaoke-cms/astro/layouts/DefaultPage.astro';
|
|
6
|
+
import ModuleLoader from '@karaoke-cms/astro/components/ModuleLoader.astro';
|
|
7
|
+
import { siteTitle } from 'virtual:karaoke-cms/config';
|
|
8
|
+
|
|
9
|
+
export async function getStaticPaths() {
|
|
10
|
+
const docs = await getCollection('docs', ({ data }) => data.publish === true);
|
|
11
|
+
return docs.map(entry => ({
|
|
12
|
+
params: { slug: entry.id },
|
|
13
|
+
props: { entry },
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { entry } = Astro.props;
|
|
18
|
+
const { Content } = await render(entry);
|
|
19
|
+
|
|
20
|
+
// All docs for the sidebar, sorted alphabetically by title
|
|
21
|
+
const allDocs = (await getCollection('docs', ({ data }) => data.publish === true))
|
|
22
|
+
.sort((a, b) => a.data.title.localeCompare(b.data.title));
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<DefaultPage title={`${entry.data.title} — ${siteTitle}`} description={entry.data.description} type="article">
|
|
26
|
+
<nav class="docs-sidebar" slot="left">
|
|
27
|
+
<ul class="docs-sidebar-list">
|
|
28
|
+
{allDocs.map(doc => (
|
|
29
|
+
<li class="docs-sidebar-item">
|
|
30
|
+
<a href={`/docs/${doc.id}`} aria-current={doc.id === entry.id ? 'page' : undefined}>
|
|
31
|
+
{doc.data.title}
|
|
32
|
+
</a>
|
|
33
|
+
</li>
|
|
34
|
+
))}
|
|
35
|
+
</ul>
|
|
36
|
+
</nav>
|
|
37
|
+
<article class="docs-article">
|
|
38
|
+
{entry.data.featured_image && (
|
|
39
|
+
<img src={entry.data.featured_image} alt="" class="docs-article-image" />
|
|
40
|
+
)}
|
|
41
|
+
<h1 class="docs-article-title">{entry.data.title}</h1>
|
|
42
|
+
{entry.data.tags && entry.data.tags.length > 0 && (
|
|
43
|
+
<div class="docs-tag-list docs-article-meta">
|
|
44
|
+
{entry.data.tags.map(tag => (
|
|
45
|
+
<a href={`/tags/${tag}`} class="docs-tag">#{tag}</a>
|
|
46
|
+
))}
|
|
47
|
+
</div>
|
|
48
|
+
)}
|
|
49
|
+
<div class="prose">
|
|
50
|
+
<Content />
|
|
51
|
+
</div>
|
|
52
|
+
</article>
|
|
53
|
+
<ModuleLoader comments={entry.data.comments} />
|
|
54
|
+
</DefaultPage>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
// TODO: use mount path from virtual module once mount-aware routing is implemented.
|
|
3
|
+
// For now, mount is assumed to be /docs.
|
|
4
|
+
import { getCollection } from 'astro:content';
|
|
5
|
+
import DefaultPage from '@karaoke-cms/astro/layouts/DefaultPage.astro';
|
|
6
|
+
import { siteTitle } from 'virtual:karaoke-cms/config';
|
|
7
|
+
|
|
8
|
+
const docs = (await getCollection('docs', ({ data }) => data.publish === true))
|
|
9
|
+
.sort((a, b) => a.data.title.localeCompare(b.data.title));
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<DefaultPage title={`Docs — ${siteTitle}`}>
|
|
13
|
+
<div class="docs-home">
|
|
14
|
+
<h1>Docs</h1>
|
|
15
|
+
{docs.length > 0 ? (
|
|
16
|
+
<ul class="docs-home-list">
|
|
17
|
+
{docs.map(doc => (
|
|
18
|
+
<li class="docs-home-item">
|
|
19
|
+
<a href={`/docs/${doc.id}`}>{doc.data.title}</a>
|
|
20
|
+
</li>
|
|
21
|
+
))}
|
|
22
|
+
</ul>
|
|
23
|
+
) : (
|
|
24
|
+
<div class="empty-state">
|
|
25
|
+
<p>No docs published yet.</p>
|
|
26
|
+
<p>Create a Markdown file in your vault's <code>docs/</code> folder and set <code>publish: true</code> in the frontmatter to make it appear here.</p>
|
|
27
|
+
</div>
|
|
28
|
+
)}
|
|
29
|
+
</div>
|
|
30
|
+
</DefaultPage>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
// TODO: use mount path from virtual module once mount-aware routing is implemented.
|
|
3
|
+
// For now, mount is assumed to be /docs.
|
|
4
|
+
import { getCollection } from 'astro:content';
|
|
5
|
+
import DefaultPage from '@karaoke-cms/astro/layouts/DefaultPage.astro';
|
|
6
|
+
import { siteTitle } from 'virtual:karaoke-cms/config';
|
|
7
|
+
|
|
8
|
+
const docs = (await getCollection('docs', ({ data }) => data.publish === true))
|
|
9
|
+
.sort((a, b) => a.data.title.localeCompare(b.data.title));
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<DefaultPage title={`All Docs — ${siteTitle}`}>
|
|
13
|
+
<div class="docs-list">
|
|
14
|
+
<h1>All Docs</h1>
|
|
15
|
+
{docs.length > 0 ? (
|
|
16
|
+
<ul>
|
|
17
|
+
{docs.map(doc => (
|
|
18
|
+
<li class="docs-list-item">
|
|
19
|
+
<a href={`/docs/${doc.id}`}>{doc.data.title}</a>
|
|
20
|
+
</li>
|
|
21
|
+
))}
|
|
22
|
+
</ul>
|
|
23
|
+
) : (
|
|
24
|
+
<div class="empty-state">
|
|
25
|
+
<p>No docs published yet.</p>
|
|
26
|
+
<p>Create a Markdown file in your vault's <code>docs/</code> folder and set <code>publish: true</code> in the frontmatter to make it appear here.</p>
|
|
27
|
+
</div>
|
|
28
|
+
)}
|
|
29
|
+
</div>
|
|
30
|
+
</DefaultPage>
|
package/src/schema.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Frontmatter schema for docs pages.
|
|
5
|
+
* All fields except `title` are optional — docs validate before and after AI enrichment.
|
|
6
|
+
*/
|
|
7
|
+
export const docsSchema = z.object({
|
|
8
|
+
title: z.string(),
|
|
9
|
+
publish: z.boolean().optional().default(false),
|
|
10
|
+
date: z.coerce.date().optional(),
|
|
11
|
+
author: z.union([z.string(), z.array(z.string())]).optional(),
|
|
12
|
+
featured_image: z.string().optional(),
|
|
13
|
+
// v0.2 AI-enriched fields
|
|
14
|
+
tags: z.array(z.string()).optional(),
|
|
15
|
+
description: z.string().optional(),
|
|
16
|
+
reading_time: z.number().optional(),
|
|
17
|
+
related: z.array(z.string()).optional(),
|
|
18
|
+
// per-doc comments override
|
|
19
|
+
comments: z.boolean().optional().default(false),
|
|
20
|
+
});
|