Files
astrolabe/docs/architecture/11-learning-section.md
T

3.2 KiB

11 — The Learning Section (/learn/)

An interactive deep-dive into Vega-Lite, served at /learn/: a marketing surface separate from the app, where each lesson walks a spec from an 80%-naive version to a polished one to teach the grammar's dormant power and funnel readers into the app.

A marketing surface, like the landing

/learn/ is a third Vite entry (learn/index.htmlsrc/learn/) alongside the landing (/) and the app (/app/), under the same marketing-surface rules:

  • Reuses src/core and the landing's LandingChart only — never stores, modals, orchestration, or app components. Vega is lazy-loaded through LandingChart, so the entry stays light.
  • Out of PWA scope. The service worker is scoped to /app/, so the learning pages stay uncontrolled, always-fresh, and indexable — the point for organic-reach content.

Lessons are markdown; the renderer is general

A lesson is a .md file in src/learn/lessons/, discovered with import.meta.glob — so adding a lesson is dropping a file, with no registry to edit. A lesson is a free-form document of ordered blocks, not a fixed template:

Authored as Block Rendered by
plain markdown prose Markdown
:::progression wrapping ## stages + fenced specs progression SpecProgression
a bare fenced vega-lite block chart LandingChart
:::name … ::: callout Markdown (styled by name)

Inside a :::progression, each ## heading is a stage: heading → tab label, prose → note, the following fenced vega-lite block → spec. Inline data repeats per fenced block — there is no shared-data construct.

The pipeline

lessons/*.mdimport.meta.glob (in LearnPage) → parseLesson (core/lesson-parse) → LessonBlock[]LearnPage block dispatch → Markdown | SpecProgression | LandingChart. SpecProgression renders each stage's spec with formatSpec (core/json-format) and highlights what the stage changed with changedLines (core/spec-diff, an LCS line-diff).

Rules

  • The engine consumes plain data, so the authoring surface is swappable. SpecProgression, spec-diff, and formatSpec take parsed blocks and specs and know nothing about markdown — the parser is the only thing coupled to the .md format. The authoring format can change without touching the widget.
  • Parsing, diff, and formatting are pure and live in core (tested hardest); rendering and the markdown library live in src/learn. Core stays dependency-freemarked is imported only by src/learn/Markdown.
  • dangerouslySetInnerHTML renders first-party lesson files (repo content authored by us), never user input — not an XSS surface.
  • Lesson specs are fenced JSON parsed with JSON.parse; the source pane re-formats them with formatSpec so the shown JSON matches the editor's house style.