Overview
The parser takes markdown input and produces aParsedLesson object:
LessonStep contains:
- Narration blocks: Text read aloud by TTS
- Visualization blocks: Code, data structures, diagrams, charts, previews
- Triggers: Commands that fire at word positions to control the stage
- Scene sequence: Timeline of stage states
Parsing Pipeline
The parser runs through these stages:Markdown to AST
Uses
unified and remark-parse to convert markdown into an abstract syntax tree (MDAST).Extract frontmatter
Parses YAML frontmatter to extract lesson metadata:If no title is provided, defaults to “Untitled”.
Segment into steps
Splits the lesson at H1 headings (Steps are the top-level structural unit. Think of them as chapters or scenes.
#). Each H1 becomes a step.Parse step contents
Within each step, separates:
- Paragraphs → Narration blocks
- Code fences → Visualization blocks
Extract triggers
Scans narration text for inline triggers using Triggers are stored with their word positions for timing during playback.
{{...}} syntax.Parse visualization blocks
Each code fence is parsed based on its language tag:
code→ Code visualizationdata→ Data structure visualizationdiagram→ Graph diagramchart→ Chart/graphmath→ LaTeX mathpreview→ HTML/React preview
meta string:Build scene sequence
Replays all triggers in order to compute the sequence of scene states.Each scene represents what’s visible on stage at a specific moment.
Key Parser Files
parse-lesson.ts
Purpose: Main orchestrator for lesson parsing.
Entry point:
- Parse markdown to AST
- Extract frontmatter (title)
- Split into steps at H1 headings
- For each step, call
buildStep() - Return
ParsedLessonwith steps and diagnostics
buildStep()
Builds a single step from MDAST nodes:
LessonStep with:
id(e.g.,"step-0")titlenarration(array ofNarrationBlock)visualizations(map of block name → block data)scenes(array ofSceneState)
parse-data.ts
Purpose: Parses data structure visualizations.
Supported types:
- Array:
[1, 2, 3] - Linked list:
1 -> 2 -> 3 - Tree: Nested JSON-like syntax
- Graph: Node and edge definitions
- Sequential animations:
seq { ... }DSL
- Detect if content starts with
seq { - If yes, parse as sequential DSL (see
parse-seq.ts) - Otherwise, parse as static data structure
- Return
DataStateobject
parse-seq.ts
Purpose: Parses the sequential animation DSL for data structures.
Syntax:
push(value)- Add to endpop()- Remove from endinsert(index, value)- Insert at indexremove(index)- Remove at indexset(index, value)- Update at index
DataState objects (one per line).
Parser implementation:
- Split content by newlines
- For each line:
- If it’s the first line, parse as initial state
- Otherwise, parse operation and result state
- Build data API for each state (pointers, highlights, etc.)
- Return sequence of states
parse-diagram.ts
Purpose: Parses node-edge diagrams.
Syntax:
->) define directed edges. Nodes are auto-detected.
Metadata:
hierarchical(default)force-directedtree
parse-chart.ts
Purpose: Parses chart/graph visualizations.
Syntax:
CSV-like format:
linebarscatter
parse-math.ts
Purpose: Parses LaTeX math expressions.
Syntax:
parse-preview.ts
Purpose: Parses HTML/React preview blocks.
Syntax:
parse-regions.ts
Purpose: Parses region definitions for sub-element addressing.
Syntax:
Regions are defined in the meta string:
build-scenes.ts
Purpose: Computes the sequence of scene states by replaying triggers.
Algorithm:
- Start with empty scene (nothing visible)
- For each trigger in order:
- Apply trigger verb (show, hide, transform, etc.)
- Compute new scene state
- Store in timeline
- Return array of
SceneStateobjects
Trigger Parsing
Triggers are extracted from narration text using regular expressions. Syntax:show/show-group- Reveal blockshide/hide-group- Remove blockstransform- Morph one block into anotherclear- Remove all blockssplit/unsplit- Toggle split-screen modefocus- Highlight a block or regionpulse- Pulse animationtrace- Draw a pathannotate- Add a labelzoom- Change zoom level
Animation Parsing
File:parse-animation.ts
Parses animation tokens from trigger arguments:
fadeslideslide-upgrowtypewriternone
ease-outease-in-outspringlinearrevealemphasishandoff
Error Handling and Diagnostics
When parsing errors occur, diagnostics are collected:Type System
Location:src/types/lesson.ts
All IR types are defined here:
ParsedLessonLessonStepNarrationBlockVisualizationState(union of all visualization types)CodeStateDataStateDiagramStateChartStateMathStatePreviewStateTrigger(union of all trigger verbs)SceneStateAnimationOverrideSlotEnterEffect
- All fields are
readonly(immutable) - No optional fields in the domain model (use explicit defaults or tagged unions)
- Use discriminated unions for variant types (e.g.,
kind: 'code' | 'data' | ...)
Testing the Parser
Currently no automated tests. Manual testing:- Create a test lesson in
data/lessons/ - Open in dev app
- Verify parsing in DevTools console:
Future Enhancements
- Error recovery: Continue parsing after errors
- Syntax highlighting: Highlight markdown syntax errors in authoring UI
- Type validation: Validate trigger arguments (e.g., block names exist)
- Auto-completion: Suggest block names in triggers
- LSP for authoring: Language server for course authoring
Next Steps
Presentation Engine
Learn how parsed lessons are played back
Frontend
Explore the React frontend
Backend
Dive into the Rust backend
Architecture
High-level system overview