@karaoke-cms/module-docs 0.9.8 → 0.11.2

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 CHANGED
@@ -49,19 +49,50 @@ All routes are relative to `mount`:
49
49
 
50
50
  ```yaml
51
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)
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 path, absolute path, URL,
57
+ # or Obsidian wiki link: "[[hero.jpg]]"
58
+ description: "..." # optional — OG tags, AI-enriched
59
+ tags: [setup, guide] # optional — AI-enriched
60
+ reading_time: 3 # optional — AI-enriched, minutes
61
+ related: [slug-a, slug-b] # optional — AI-enriched, slugs
62
+ comments: false # optional — per-doc Giscus override (off by default)
62
63
  ---
63
64
  ```
64
65
 
66
+ ### `featured_image` formats
67
+
68
+ All of the following work in `featured_image`:
69
+
70
+ ```yaml
71
+ featured_image: "./images/hero.jpg" # relative to the note
72
+ featured_image: "/docs/hero.jpg" # vault-absolute path
73
+ featured_image: "https://example.com/x.jpg" # remote URL
74
+ featured_image: "[[hero.jpg]]" # Obsidian wiki link — resolved via vault lookup
75
+ ```
76
+
77
+ When using a wiki link, the image is served from `/media/` during dev and copied to `dist/media/` at build.
78
+
79
+ ## What's new in 0.10.3
80
+
81
+ - **Warning for `mount: '/'`** — calling `docs({ mount: '/' })` without an explicit `folder` now prints a warning, because the folder silently defaults to `"docs"` in this case. Pass `folder` explicitly to suppress it: `docs({ mount: '/', folder: 'docs' })`.
82
+
83
+ ## What's new in 0.10.2
84
+
85
+ - **Multiple named docs sections** — call `docs()` multiple times with different `mount`, `folder`, and `label` values to create independent docs areas from separate vault folders.
86
+ - **`folder` option** — map a docs instance to any vault subfolder (defaults to the mount path without the leading slash).
87
+ - **`id` option** — override the Astro collection name (defaults to folder path, slashes replaced with hyphens).
88
+ - **`label` option** — set a human-readable section title shown in navigation and page headings (defaults to id in Title Case).
89
+ - **Section switcher** — when two or more sections are active, a nav row appears above the sidebar so readers can jump between sections.
90
+ - **Auto-registered collections** — pass your full karaoke config to `makeCollections({ config })` in `content.config.ts` and every docs instance registers automatically. No manual changes needed when adding a second section.
91
+
92
+ ## What's new in 0.10.0
93
+
94
+ - **`[[wiki link]]` in `featured_image`** — `featured_image: "[[hero.jpg]]"` now works on doc pages; images are resolved via vault path lookup and served from `/media/`
95
+
65
96
  ## What's new in 0.9.5
66
97
 
67
98
  - **`featured_image` support** added to the docs schema
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@karaoke-cms/module-docs",
3
3
  "type": "module",
4
- "version": "0.9.8",
4
+ "version": "0.11.2",
5
5
  "description": "Docs module for karaoke-cms — documentation pages with sidebar navigation",
6
6
  "main": "./src/index.ts",
7
7
  "exports": {
@@ -23,13 +23,15 @@
23
23
  ],
24
24
  "peerDependencies": {
25
25
  "astro": ">=6.0.0",
26
- "@karaoke-cms/astro": "^0.9.8"
26
+ "@karaoke-cms/contracts": "^0.11.2"
27
27
  },
28
28
  "devDependencies": {
29
- "@karaoke-cms/astro": "workspace:*",
30
- "astro": "^6.0.8"
29
+ "astro": "^6.0.8",
30
+ "vitest": "^4.1.1",
31
+ "@karaoke-cms/astro": "0.11.2",
32
+ "@karaoke-cms/contracts": "0.11.2"
31
33
  },
32
34
  "scripts": {
33
- "test": "echo \"Stub — no tests\""
35
+ "test": "vitest run test/docs-factory.test.js"
34
36
  }
35
- }
37
+ }
@@ -1,20 +1,30 @@
1
+ import type { CssContract } from '@karaoke-cms/contracts';
2
+
1
3
  /**
2
4
  * CSS class names the docs module promises to use in its markup.
3
5
  * Themes implement these classes to style the docs section.
6
+ *
7
+ * `required: true` — the theme MUST implement this class.
8
+ * `required: false` — optional / progressive enhancement.
4
9
  */
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;
10
+ export const cssContract: CssContract = {
11
+ // Home / overview
12
+ 'docs-home': { description: 'Docs home page wrapper', required: true },
13
+ 'docs-home-list': { description: 'List of doc links on the home page', required: true },
14
+ 'docs-home-item': { description: 'Individual doc link on the home page', required: true },
15
+ // Full list page
16
+ 'docs-list': { description: 'Full docs listing page wrapper', required: false },
17
+ 'docs-list-item': { description: 'Individual row in the full docs list', required: false },
18
+ // Article page
19
+ 'docs-article': { description: 'Individual doc article wrapper', required: true },
20
+ 'docs-article-image': { description: 'Featured image inside a doc article', required: false },
21
+ 'docs-article-title': { description: 'Doc article title', required: true },
22
+ 'docs-article-meta': { description: 'Date/author meta row on a doc article', required: false },
23
+ // Tags
24
+ 'docs-tag': { description: 'Tag chip on doc card or article', required: false },
25
+ 'docs-tag-list': { description: 'Wrapper around a list of doc tag chips', required: false },
26
+ // Sidebar navigation
27
+ 'docs-sidebar': { description: 'Sidebar nav wrapper', required: true },
28
+ 'docs-sidebar-list': { description: 'List of nav links inside the sidebar', required: true },
29
+ 'docs-sidebar-item': { description: 'Individual nav link in the sidebar', required: true },
30
+ };
package/src/index.ts CHANGED
@@ -1,40 +1,84 @@
1
- import { defineModule } from '@karaoke-cms/astro';
1
+ import type { ModuleInstance } from '@karaoke-cms/contracts';
2
2
  import { cssContract } from './css-contract.js';
3
3
 
4
4
  export { cssContract } from './css-contract.js';
5
5
  // docsSchema is exported from '@karaoke-cms/module-docs/schema' to avoid
6
6
  // pulling zod into the config-loading context.
7
7
 
8
+ export interface DocsConfig {
9
+ /** Mount path for this docs section. Default: '/docs'. */
10
+ mount?: string;
11
+ /** Vault subfolder containing the docs markdown files. Default: mount path without leading slash. */
12
+ folder?: string;
13
+ /**
14
+ * Unique identifier for this docs instance. Used as the Astro collection name.
15
+ * Default: folder path with slashes replaced by hyphens.
16
+ */
17
+ id?: string;
18
+ /** Human-readable label shown in navigation and page titles. Default: id in Title Case. */
19
+ label?: string;
20
+ /** When false, this instance is excluded from the build. Default: true. */
21
+ enabled?: boolean;
22
+ layout?: string;
23
+ sidebarStyle?: string;
24
+ }
25
+
8
26
  /**
9
27
  * Docs module — documentation pages with sidebar navigation.
10
28
  *
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
29
+ * Returns a ModuleInstance that karaoke() uses to code-generate per-instance
30
+ * .astro page files and inject them as routes. Multiple instances may be
31
+ * configured to serve separate docs sections from different vault folders.
32
+ *
33
+ * Publishes three routes per instance:
34
+ * - `{mount}` — home page listing all doc titles
35
+ * - `{mount}/list` — standalone full docs list
36
+ * - `{mount}/[slug]` — individual doc with left-sidebar nav
15
37
  *
16
38
  * @example
17
- * // karaoke.config.ts
39
+ * // karaoke.config.ts — default section
18
40
  * import { docs } from '@karaoke-cms/module-docs';
19
41
  * export default defineConfig({
20
- * modules: [docs({ mount: '/docs' })],
21
42
  * theme: themeDefault({ implements: [docs({ mount: '/docs' })] }),
22
43
  * });
44
+ *
45
+ * @example
46
+ * // karaoke.config.ts — two independent sections
47
+ * export default defineConfig({
48
+ * theme: themeDefault({ implements: [docs({ mount: '/docs' })] }),
49
+ * modules: [docs({ mount: '/api-docs', folder: 'api-reference', label: 'API Reference' })],
50
+ * });
23
51
  */
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
- });
52
+ export function docs(config: DocsConfig = {}): ModuleInstance {
53
+ const mount = (config.mount ?? '/docs').replace(/\/$/, '');
54
+ const rawFolder = mount.replace(/^\//, '');
55
+ if (config.folder === undefined && rawFolder === '') {
56
+ console.warn(
57
+ '[karaoke-cms] docs({ mount: \'/\' }): folder defaults to "docs". ' +
58
+ 'Pass folder explicitly (e.g. docs({ mount: \'/\', folder: \'docs\' })) to suppress this warning.',
59
+ );
60
+ }
61
+ const folder = config.folder ?? (rawFolder || 'docs');
62
+ const id = config.id ?? (folder.replace(/\//g, '-') || 'docs');
63
+ const label = config.label ?? (id.charAt(0).toUpperCase() + id.slice(1).replace(/-/g, ' '));
64
+ return {
65
+ _type: 'module-instance',
66
+ id,
67
+ mount,
68
+ enabled: config.enabled ?? true,
69
+ collection: {
70
+ name: id,
71
+ folder,
72
+ label,
73
+ layout: config.layout ?? 'default',
74
+ sidebarStyle: config.sidebarStyle ?? 'tree',
75
+ },
76
+ // routes is empty — karaoke() generates per-instance pages via codegen
77
+ routes: [],
78
+ menuEntries: [{ id, name: label, path: mount, section: 'main', weight: 20 }],
79
+ cssContract,
80
+ hasDefaultCss: false,
81
+ defaultCssPath: undefined,
82
+ scaffoldPages: undefined,
83
+ };
84
+ }
@@ -5,6 +5,7 @@ import { getCollection, render } from 'astro:content';
5
5
  import DefaultPage from '@karaoke-cms/astro/layouts/DefaultPage.astro';
6
6
  import ModuleLoader from '@karaoke-cms/astro/components/ModuleLoader.astro';
7
7
  import { siteTitle } from 'virtual:karaoke-cms/config';
8
+ import { resolveWikiImage } from '@karaoke-cms/astro';
8
9
 
9
10
  export async function getStaticPaths() {
10
11
  const docs = await getCollection('docs', ({ data }) => data.publish === true);
@@ -36,7 +37,7 @@ const allDocs = (await getCollection('docs', ({ data }) => data.publish === true
36
37
  </nav>
37
38
  <article class="docs-article">
38
39
  {entry.data.featured_image && (
39
- <img src={entry.data.featured_image} alt="" class="docs-article-image" />
40
+ <img src={resolveWikiImage(entry.data.featured_image)} alt="" class="docs-article-image" />
40
41
  )}
41
42
  <h1 class="docs-article-title">{entry.data.title}</h1>
42
43
  {entry.data.tags && entry.data.tags.length > 0 && (