mirror of
https://github.com/HananoshikaYomaru/openfreebooks.git
synced 2026-11-12 20:45:52 +00:00
131 lines
5.1 KiB
Markdown
131 lines
5.1 KiB
Markdown
# 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.
|