@karaoke-cms/astro
v0.18.1
Published
Core Astro integration for karaoke-cms — virtual config, wikilinks, handbook routes
Maintainers
Readme
@karaoke-cms/astro
Core Astro integration for karaoke-cms. Wires up themes, modules, collections, menus, Obsidian embed handling, and the virtual config module at build time. Every karaoke-cms project depends on this package.
Installation
npm install @karaoke-cms/astroUsage
// astro.config.mjs
import { defineConfig } from 'astro/config';
import karaoke from '@karaoke-cms/astro';
import karaokeConfig from './karaoke.config.ts';
export default defineConfig({
site: 'https://your-site.pages.dev',
integrations: [karaoke(karaokeConfig)],
});// karaoke.config.ts
import { defineConfig } from '@karaoke-cms/astro';
import { loadEnv } from '@karaoke-cms/astro/env';
import { blog } from '@karaoke-cms/module-blog';
import { docs } from '@karaoke-cms/module-docs';
import { themeDefault } from '@karaoke-cms/theme-default';
const env = loadEnv(new URL('.', import.meta.url));
export default defineConfig({
vault: env.KARAOKE_VAULT,
title: 'My Site',
description: 'What this site is about.',
theme: themeDefault(),
modules: [blog({ mount: '/blog' }), docs({ mount: '/docs' })],
});Configuration
All fields are optional. Defaults produce a working site with no content.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| vault | string | project root | Path to the Obsidian vault directory (absolute or relative) |
| title | string | 'Karaoke' | Site title for browser tab and nav |
| description | string | '' | Site description for RSS and OG tags |
| theme | ThemeInstance | — | Theme from themeDefault() or similar |
| modules | ModuleInstance[] | [] | Additional modules not covered by the theme |
| comments | CommentsConfig | — | Giscus comments config |
| collections | Record<string, CollectionConfig> | — | Per-collection mode overrides |
| layout.regions | { top, left, right, bottom } | — | Region component lists |
Obsidian embed syntax
Markdown files can use Obsidian's ![[file]] embed syntax. The integration handles it automatically:
Image embeds
![[photo.jpg]] — embedded image (Astro-optimized)
![[photo.jpg|300]] — image with width hint → <img width="300">
![[photo.jpg|300x200]] — image with width and height → <img width="300" height="200">
![[photo.jpg|alt text]] — image with alt textImage files are resolved in this order:
- Same folder as the note
- Vault
attachmentFolderPathfrom.obsidian/app.json - Vault root
Resolved images go through Astro's image optimisation pipeline.
Audio and video embeds
![[recording.mp3]] — → <audio controls>
![[clip.mp4]] — → <video controls>Audio and video files are served from /media/ in dev and copied to dist/media/ at build time.
Wiki links in featured_image frontmatter
featured_image: "[[hero.jpg]]"The wiki link is resolved using the same vault path lookup as inline embeds and rewritten to /media/hero.jpg. Use resolveWikiImage() in your templates — see below.
resolveWikiImage(value)
A utility for templates that render featured_image. Call it on the raw frontmatter value — it rewrites [[file.jpg]] to /media/file.jpg and passes regular paths and URLs through unchanged.
import { resolveWikiImage } from '@karaoke-cms/astro';
// In an Astro template:
<img src={resolveWikiImage(entry.data.featured_image)} alt={entry.data.title} />This is necessary because Astro sets entry.data from validated frontmatter at collection-load time, before the remark plugin runs. Templates must call resolveWikiImage() to get the resolved URL.
Exports
@karaoke-cms/astro — main integration
karaoke(config)— Astro integration (default export)defineConfig(config)— identity wrapper for type inferencedefineModule(def)— create a module factorydefineTheme(def)— create a theme factoryresolveWikiImage(value)— resolve[[wiki link]]to/media/URL for use in templates
@karaoke-cms/astro/env
loadEnv(dir)— reads.env.defaultand.envfromdir, returns merged record
@karaoke-cms/astro/collections
makeCollections(root, vault?, collections?)— creates Astro content collections from the vault
virtual:karaoke-cms/config
Available in any page or component at build time:
import {
siteTitle, // string
siteDescription, // string
resolvedCollections, // Record<string, { modes, label, enabled }>
resolvedMenus, // Record<string, { name, orientation, entries }>
resolvedLayout, // { regions: { top, left, right, bottom } }
resolvedModules, // { comments: { enabled, repo, repoId, category, categoryId } }
blogMount, // string — the blog module's mount path
} from 'virtual:karaoke-cms/config';Add to src/env.d.ts for TypeScript types:
/// <reference types="@karaoke-cms/astro/client" />Menus
Define menus in {vault}/_website/menus.yaml. When absent, defaults are generated: a main menu with Blog/Docs/Tags and a footer menu with the RSS link. Entries support when: collection:name to hide automatically when a collection is empty.
Layout regions
Four regions (top, left, right, bottom) accept component lists from karaoke.config.ts:
layout: {
regions: {
top: { components: ['header', 'main-menu'] },
right: { components: ['recent-posts'] },
bottom: { components: ['footer'] },
}
}Available components: 'header', 'main-menu', 'search', 'recent-posts', 'footer'.
What's new in 0.18.1
menus.yamllocation changed — the navigation config file moved from{vault}/karaoke-cms/config/menus.yamlto{vault}/_website/menus.yaml. If you have an existingmenus.yaml, move it to_website/menus.yamlin your vault root.
What's new in 0.18.0
- Nested docs routes — docs pages now use a
[...slug]rest parameter instead of[slug], so subdirectories within a docs section resolve correctly. No config changes needed. - Section switcher everywhere — the styled nav showing all active docs sections now appears on doc pages, section home pages, and section list pages (previously doc pages only).
- DocsTree sidebar cleanup —
index.mdleaf nodes are hidden from the sidebar and hoisted to their parent directory node, reducing visual clutter. - Wikilink resolution fix — relative links inside docs sections are now resolved against the module's mount path, so cross-page links work correctly in any docs section regardless of URL prefix.
- Blog folder config in link normalization — relative link normalization now respects the blog module's configured folder, fixing broken links when the blog folder differs from the default.
modeson module instances — modules can now declaremodes: ['dev']ormodes: ['dev', 'prod']directly on theModuleInstance, replacing the need forcollections.yamloverrides.
What's new in 0.17.0
- Submenu support —
menus.yamlentries now supportparent: <id>to nest under a root entry as a hover/click submenu. Usestatic: "Label"for a non-interactive parent label. TheMenu.astrocomponent renders a toggle button for keyboard accessibility. themeDefault()takes no arguments — modules are passed viamodules: []indefineConfig(), not viaimplements: []inside the theme call. Updatekaraoke.config.tsif you were using the old pattern.- Per-module comments default —
blog()anddocs()acceptcomments?: boolean. Blog defaults totrue, docs defaults tofalse. Individual pages can still override via frontmattercomments: true/false. - Docs array form —
docs([...])now accepts aDocsSection[]array, returning oneModuleInstanceper section. Each section can specifyweightandparentfor menu ordering and nesting.
What's new in 0.10.3
- Vault path warning — if
KARAOKE_VAULTpoints to a directory that doesn't exist, a clear warning is printed at startup instead of silently building a site with empty collections. The message shows the resolved path and points to.env/.env.default. menus.yamllive reload — editing{vault}/_website/menus.yamlwhile the dev server is running now triggers an immediate full-page reload. No more restartingnpm run devafter menu changes.- Security: path traversal guard hardened — the docs codegen path-traversal check now uses
path.normalize()on both sides of the comparison, so the guard holds correctly on Windows and with symlinked paths.
What's new in 0.10.2
- Multiple named docs sections — run
docs({ mount: '/api-docs', folder: 'api-reference', label: 'API Reference' })alongsidedocs({ mount: '/docs' }). Each section gets its own sidebar, URL space, and Astro content collection. Routes are code-generated per instance at build time. - Auto-registered docs collections — pass your full config to
makeCollections({ config })incontent.config.tsand all docs instances register automatically. No manual sync required when adding a second section. virtual:karaoke-cms/docs-sections— new Vite virtual module exporting all active docs section specs. Import it in any page to build section switchers or cross-section links.- Section switcher in doc pages — when two or more docs sections are active, a nav row lists them with the current one highlighted.
- Tags and RSS are section-aware —
/tags,/tags/[tag], and/rss.xmlnow aggregate entries from every active docs section. - Fix: dev-only collection menu entries — entries with
when: collection:namenow correctly show in dev mode when the collection exists but has no published entries.
What's new in 0.10.0
- Obsidian
![[image]]embed support —![[photo.jpg]]in any markdown note is resolved via vault path lookup and goes through Astro's image optimisation pipeline - Size hints —
![[photo.jpg|300]]setswidth="300";![[photo.jpg|300x200]]sets both width and height - Audio and video embeds —
![[recording.mp3]]→<audio controls>,![[clip.mp4]]→<video controls>; files served from/media/in dev, emitted todist/media/at build featured_imagewiki link support —featured_image: "[[hero.jpg]]"in frontmatter is resolved to/media/hero.jpgvia vault path lookupresolveWikiImage(value)export — utility for templates to rewrite[[file.jpg]]frontmatter values to the correct/media/URL at render time
What's new in 0.9.5
isThemeInstancetype-guard added — fixes a runtime crash in theastro:config:setuphook when aThemeInstancewas passed- Module array API —
modulesin config is nowModuleInstance[](was a legacy object shape in some docs) - Tighter
astro.config.mjserror handling — missingkaraoke.config.tsis now distinguished from missing dependencies of that file; the latter now surfaces the real error instead of silently falling back to empty config
