mirror of
https://github.com/kbenestad/mdcms.git
synced 2026-06-18 15:24:32 +00:00
Documents the two-part architecture (mdcms.py CLI + index.html renderer), menu option reference, content structure, frontmatter fields, category system, nav.yml behaviour, dynamic post tags, known limitations, and key implementation quirks (non-general YAML parsers, category code validation). https://claude.ai/code/session_018gathVoTZhBFDJVp1mVaTc
127 lines
6.2 KiB
Markdown
127 lines
6.2 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## What this project is
|
|
|
|
MD-CMS is a markdown-based static site system with two distinct parts:
|
|
|
|
1. **`mdcms.py`** — a zero-dependency Python 3 CLI tool (standard library only). It scans content, generates `nav.yml` and `search.json`, validates config, and packages a zip for deployment.
|
|
2. **`website/index.html`** — a single-file browser renderer that reads markdown, config, and nav at runtime entirely client-side. There is no build pipeline, no compilation, no server.
|
|
|
|
The `website/` folder is the deployable artifact. `mdcms.py` lives outside it.
|
|
|
|
## Running the CLI
|
|
|
|
```bash
|
|
python3 mdcms.py
|
|
```
|
|
|
|
This opens an interactive menu. Key options:
|
|
|
|
| Option | Action |
|
|
|--------|--------|
|
|
| 1 | Validate config + nav, rebuild search.json, export `website.zip` |
|
|
| 2 | Interactive wizard to create `config.yml` and folder structure from scratch |
|
|
| 3 | Scan `pages/` and `posts/`, write `nav.yml` and `search.json` |
|
|
| 4 | Rebuild `search.json` only (faster, for content-only updates) |
|
|
| 5 | Check and interactively fix `config.yml` issues |
|
|
| 6 | Check and interactively fix `nav.yml` issues |
|
|
| 7 | Register/switch website paths (stored in `~/.mdcms/paths.json`) |
|
|
| 8 | Start `python3 -m http.server 8800` and open browser |
|
|
|
|
**Local preview:** Always use option 8 (or run `python3 -m http.server 8800` in `website/`) rather than opening `index.html` directly — browsers block local file access due to CORS.
|
|
|
|
## Architecture of `mdcms.py`
|
|
|
|
The file is a single-module Python script with these logical layers, in order:
|
|
|
|
1. **Path registry** — `~/.mdcms/paths.json` stores named project paths. `active_path()` returns the currently selected project.
|
|
2. **Frontmatter parser** (`parse_frontmatter`) — reads `---` YAML blocks with a hand-rolled parser (no PyYAML). Returns a flat `{key: value}` dict with type coercion (bool, int, quoted strings).
|
|
3. **Category system** — category codes are parsed from `config.yml` by `parse_config_categories()`. Variant files use `basename.<code>.md` naming (e.g. `about.nb.md`). `identify_variant()` only recognises a suffix as a category code if it appears in the declared code list.
|
|
4. **Scanner** (`scan_and_categorize`) — walks `pages/` or `posts/`, skips drafts (`draft: true` in frontmatter), returns records including first 5000 chars of body for search indexing.
|
|
5. **Nav/search generators** — `generate_nav_yml()` and `generate_search_json()` produce the output files. `parse_nav_yml()` is a hand-rolled parser for reading back nav.yml (not a general YAML parser — only handles the exact format it emits).
|
|
6. **Validators** (`validate_config`, `validate_nav`) — return lists of `{field, issue, current}` dicts for display; used in options 1, 5, and 6.
|
|
7. **Menu actions** (`do_*` functions) — one per menu option, called by `main_menu()`.
|
|
|
|
## Content structure inside `website/`
|
|
|
|
```
|
|
website/
|
|
index.html ← renderer (edit to change site behaviour)
|
|
config.yml ← required: sitename, navigation; rest optional
|
|
nav.yml ← generated; manual edits to section metadata are preserved on rebuild
|
|
search.json ← generated
|
|
pages/
|
|
home.md ← default landing page
|
|
about.md ← default variant
|
|
about.nb.md ← Norwegian variant (category suffix = nb)
|
|
posts/
|
|
2025-01-01-my-first-post.md
|
|
assets/
|
|
fonts/
|
|
images/
|
|
```
|
|
|
|
## Page frontmatter fields
|
|
|
|
All optional except `title`:
|
|
|
|
```yaml
|
|
---
|
|
title: Page Title
|
|
sort: 100 # controls nav ordering (lower = higher)
|
|
section-id: blog # assigns page to a nav section
|
|
draft: true # exclude from nav and search
|
|
author: Name
|
|
date: 2025-01-01
|
|
datetime: 2025-01-01 13:00 # use this for posts (not `date` alone — see known limitations)
|
|
modified: 2025-01-15 09:00
|
|
keywords: foo, bar
|
|
description: Short description for search
|
|
language: en
|
|
---
|
|
```
|
|
|
|
## nav.yml structure
|
|
|
|
Sections and pages are separate lists. `mdcms.py` preserves manual edits to section fields (`defaultname`, `sort`, `parent`, `parent-sort`, `pagesvisibility`, `categorynames`) on each rebuild. New sections are auto-created from `section-id` values found in frontmatter.
|
|
|
|
`pagesvisibility` can be `visible`, `hidden`, or `draft` (draft excludes pages from `search.json`).
|
|
|
|
For nested navigation, set `parent: <parent-section-code>` and `parent-sort` on a section.
|
|
|
|
## Category system
|
|
|
|
- `categories-use: yes` in `config.yml` enables categories
|
|
- `default-category.code` is required when categories are enabled
|
|
- Variant files: `<base>.<code>.md` — the suffix is only treated as a category if the code is declared in config
|
|
- `categories-sectionnames: per-category` requires each section in `nav.yml` to have a `categorynames` block with an entry per category code
|
|
- RTL is set per category via `direction: rtl`
|
|
|
|
## Dynamic post tags (mdcms code blocks)
|
|
|
|
Embed post lists in pages using fenced blocks:
|
|
|
|
````markdown
|
|
```mdcms
|
|
posts-datetime-reversechronological
|
|
limit: 10
|
|
paginate: yes
|
|
```
|
|
````
|
|
|
|
Reliable tags (others are known-broken): `posts-datetime-chronological-byyearmonth`, `posts-datetime-reversechronological`. Use `datetime` frontmatter (format: `YYYY-MM-DD HH:MM`) for posts — `date` alone does not work reliably.
|
|
|
|
## Known limitations
|
|
|
|
- `mdcms.py` always expects the deployable site in a `website/` subdirectory of the registered project path.
|
|
- Most `posts-*` tag variants are broken. Only `posts-datetime-chronological-byyearmonth` and `posts-datetime-reversechronological` reliably work.
|
|
- Section headings in the nav are non-clickable (sections-sitemap is not yet implemented).
|
|
|
|
## Key implementation details
|
|
|
|
- `parse_nav_yml()` and `parse_config_categories()` are **not** general YAML parsers — they only handle the exact indentation/format that `mdcms.py` itself produces. Do not assume they handle arbitrary YAML.
|
|
- Category code validation uses `CATEGORY_CODE_RE = re.compile(r"^[a-zA-Z0-9\-]+$")` — codes must match this.
|
|
- The `samplesite/` directory is a reference implementation with multi-language categories (English, Norwegian, Arabic including RTL). It is not deployed; it exists for reference and testing.
|
|
- `website/` in the repo root is a minimal starter template (single page, no categories).
|