# Content

> Render Markdown from src/content through the koze:content virtual module

Package: Koze
Canonical: https://kuratchi.dev/docs/koze/content
Markdown: https://kuratchi.dev/docs/koze/content.md

Koze discovers Markdown files under `src/content/<name>/` and exposes them through the `koze:content` virtual module. The folder name becomes the property on `content`.

```text
src/
  content/
    docs/
      getting-started.md
      settings/api-keys.md
    changelog/
      first-release.md
```

```html
<script>
  import { content } from 'koze:content';

  const nav = await content.docs.list();
  const doc = await content.docs.render('settings/api-keys');
</script>

<aside>
  for (const item of nav) {
    <a href={item.href}>{item.title}</a>
  }
</aside>

if (doc) {
  <article>{@html doc.html}</article>
}
```

## Content files

Each Markdown file can include YAML frontmatter:

```md
---
title: API Keys
description: Create and rotate API keys.
section: Settings
order: 20
---

# API Keys

Use short-lived keys for automation.
```

The content id is derived from the file path below the group:

| File | ID |
| --- | --- |
| `src/content/docs/getting-started.md` | `getting-started` |
| `src/content/docs/settings/api-keys.md` | `settings/api-keys` |
| `src/content/docs/settings/index.md` | `settings` |
| `src/content/docs/index.md` | `index` |

Loose Markdown files directly inside `src/content/` are invalid. Put files inside a named group so the runtime API has a stable property to expose.

## API

`content.<name>.list()` returns sorted metadata for every Markdown file in the group:

```ts
const docs = await content.docs.list();
```

Each item includes:

| Field | Description |
| --- | --- |
| `id` | Path id used by `render(id)` |
| `href` | Default URL-like path, such as `/docs/settings/api-keys` |
| `file` | Source file path relative to the project root |
| `title` | Frontmatter `title`, first heading, or a titleized filename |
| `description` | Frontmatter `description`, when present |
| `section` | Frontmatter `section`, when present |
| `order` | Frontmatter `order`, defaulting to `999` |
| `headings` | Extracted Markdown headings with `depth`, `slug`, and `text` |
| `frontmatter` | Raw frontmatter object |

`content.<name>.render(id)` returns the same metadata plus the raw Markdown body and rendered HTML:

```ts
const doc = await content.docs.render('settings/api-keys');

if (doc) {
  console.log(doc.html);
}
```

Missing ids return `null`.

## Rendering

Markdown rendering uses a CommonMark/GFM pipeline internally. Tables, task lists, fenced code blocks, and heading extraction work out of the box. Raw HTML from Markdown is not passed through by default; render authored team docs as Markdown, and use Koze components/routes for richer interactive UI.

## Generated Types

`src/app.d.ts` is regenerated during dev and build. If `src/content/docs` exists, `content.docs` is typed. If `src/content/changelog` exists, `content.changelog` is typed. Folder names with dashes are still available with bracket access:

```ts
await content['release-notes'].list();
```
