openfreebooks-bookgenerator/.agents/skills/ofb/curriculum-data.md

131 lines
5.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Curriculum & catalog data
## `data/catalog.json`
Site-wide catalog chrome.
```json
{
"title": "Curriculum Catalog",
"subtitle": "Browse chapters by subject and curriculum",
"curriculums": ["Foundation", "DSE", "IB", "A-Level", "AP", "IGCSE", "Common Core"],
"subjects": [
{ "id": "math", "name": "Mathematics" }
]
}
```
| Field | Rules |
|-------|--------|
| `id` | kebab-case, URL-safe; becomes `?subject=` and path prefix `/{id}/` |
| `name` | Display label in sidebar |
| `curriculums` | Union of all filter chips; add new labels here first |
## `data/{subject}-curriculum.json`
Example: `data/math-curriculum.json`.
```json
{
"title": "Mathematics",
"subtitle": "Optional; deprecated — do not use HKDSE-only framing. Catalog chrome uses data/catalog.json.",
"intro": "Optional subject-level intro (not shown in catalog UI today)",
"intro": "Optional; was used by old book page",
"graph": {
"edges": [
{ "from": "quadratic-equations", "to": "sequences-series" },
{ "from": "quadratic-equations", "to": "functions-graphs" },
{ "from": "functions-graphs", "to": "linear-programming" }
]
},
"strands": [
{
"id": "number-algebra",
"title": "Number & Algebra",
"chapters": [
{
"slug": "quadratic-equations",
"title": "Quadratic equations",
"description": "One or two sentences shown in list and map views.",
"status": "live",
"curriculums": ["DSE", "IB"]
}
]
}
]
}
```
### Chapter fields
| Field | Required | Notes |
|-------|----------|-------|
| `slug` | yes | Matches `content/{subject}/{slug}/`; kebab-case |
| `title` | yes | Catalog list + map node label |
| `description` | yes | 1–2 sentences; shown in list and map cards (clamped to 3 lines on map) |
| `status` | yes | `live` (link to chapter) or `planned` (no link) |
| `curriculumCoverage` | yes (math) | Object mapping framework label → `core` \| `extension` \| `related` (Compare matrix + filters) |
| `curriculums` | legacy | Deprecated array; use `curriculumCoverage` instead |
| `tier` | no | `foundation` (default) or `non-foundation` — shows an **Extension** badge on cards (DSE M2-style topics) |
### Graph model (map)
| Rule | Detail |
|------|--------|
| Scope | DSE compulsory topic units (~25–35 chapters); primary/junior not in JSON yet |
| Edges | **Required** prerequisites only; `graph.edges` is the sole source (no auto chain from strand array order) |
| Scope of `from`/`to` | Any chapter slug — **cross-strand** edges allowed (arrows may cross columns) |
| Levels | **Longest-path** from roots: `level = 1 + max(level(parent))`; roots at 0 |
| List order | `strands[].chapters[]` array order is for the **list** tab only (authoring / ToC) |
| Roots | Multiple level-0 chapters per strand OK; edgeless placeholders OK while drafting |
### Graph edges
- `from` / `to` are **chapter slugs** (not node ids).
- Edges must form a **DAG** (no cycles). Map builder throws on cycles.
- One prerequisite → add one edge. Multiple parents → multiple edges into the same `to`.
- Branches: one `from` → many `to` (e.g. quadratics → sequences and functions-graphs).
- Merges: many `from` → one `to` (e.g. functions + sequences → linear programming).
### Wiring into catalog
`themes/openfreebooks/templates/catalog.html` exposes strands + graph in `#catalog-data` by loading `data/{subject.id}-curriculum.json` for each catalog subject.
```tera
{% for subject in catalog.subjects %}
{% set subject_curriculum = load_data(path="data/" ~ subject.id ~ "-curriculum.json", format="json") %}
...
"strands": {{ subject_curriculum.strands | default(value=[]) | json_encode() | safe }}
{% endfor %}
```
## Status: planned vs live
| status | Catalog list | Map node | Content required |
|--------|--------------|----------|------------------|
| `planned` | Shown, “Coming soon” badge | Card shown; title not linked | Only JSON entry |
| `live` | Full card links to chapter | Title links to chapter (`→` + underline); rest of card not clickable | `content/` + HTML partial + template wiring |
Map view is Mermaid-based (`CatalogMermaidView` + `catalog-to-mermaid.ts`) and uses `graph.edges` as the source-of-truth DAG.
## Adding a curriculum label
1. Add string to `data/catalog.json` → `curriculums`.
2. Tag chapters with that string in `curriculums` arrays.
3. Add `.catalog-badge--{name}` in `_catalog.scss` (see existing `--dse`, `--ib`, …).
4. Optional: `curriculumBadgeClass()` in `frontend/src/lib/catalog-badge.ts` (defaults exist).
## Map card heights (agents)
Do not hand-place map node positions. The pipeline is:
1. `subjectToMermaid()` builds Mermaid source from strands + `graph.edges`.
2. `CatalogMermaidView` renders with Mermaid (`theme: "base"` + hex theme variables).
3. `enhanceStrandClusterHeaders()` post-processes strand header chrome in SVG.
Card markup must stay in sync between `catalog-chapter-card.tsx` (Solid + `renderCatalogChapterCardElement`) and `_catalog.scss` (especially `.catalog-chapter-card--map`).
## Validation
- `bun run validate:curriculum` (scripted checks for slugs, curricula labels, live content paths, and DAG acyclicity).
- `bun run build` succeeds.