Skip to content

Latest commit

 

History

History
151 lines (119 loc) · 6.64 KB

File metadata and controls

151 lines (119 loc) · 6.64 KB

Course viewer

A static web front end for the courses under courses/. It reads the pipeline's output and renders it; it never writes to it.

There is one source of truth, and it is the YAML and Markdown the agents produce. This app holds no course content of its own — no database, no CMS, no duplicated copy of a lesson. Regenerate a lesson and rebuild, and the site says what the lesson now says.

Running it

cd web
npm install
npm run dev

Then open the URL it prints. Astro 7 runs the dev server as a daemon, so the command returns immediately:

npx astro dev status
npx astro dev logs
npx astro dev stop
Script Does
npm run dev dev server with live reload on any change under courses/
npm run build type-checks, then writes the static site to web/dist/
npm run preview serves web/dist/ as it will be served in production
npm run check astro check on its own

How it updates

Adding a course is dropping a directory into courses/. Nothing registers it, because nothing has a list to register it in: the route generators enumerate the directory at build time, so a course exists on the site exactly when it exists on disk.

In dev that enumeration has to be redone when the disk changes, and Astro caches getStaticPaths() results per route. A Vite plugin (src/lib/watch-courses.ts) watches courses/ and signals astro:content-changed, which is what clears that cache; a plain browser reload would not, and the page would come back with the same stale routes. Add a course, edit a lesson, delete a glossary — the open tab reflects it without a restart.

In production it is a plain static build. Rebuild to publish.

What it shows

Learner-facing artefacts only:

File Rendered as
course.yaml the course's module and lesson structure
audience.yaml who the course is for, prerequisites, misconceptions
curriculum.yaml module rationale, pacing, cognitive load, skills unlocked
outcomes.yaml per-lesson outcomes with Bloom level and evidence
<lesson>.md the lesson page
<lesson>.exercises.yaml practice, with hints revealed one at a time
<lesson>.quiz.yaml a self-marking knowledge check
glossary.yaml an alphabetised glossary linking each term to the lesson it appears in
project.yaml the capstone brief, milestones and rubric

assessment.yaml and the *.plan.yaml files are deliberately not rendered. They are the instructions the pipeline gave itself, and showing a learner the answer key and the lesson's own blueprint would undercut the artefacts that depend on them.

Prose in YAML is Markdown

Quiz prompts, exercise tasks and solutions carry fenced code blocks and inline code — a question about a diff is the diff — so those fields are rendered as Markdown rather than as text. They go through the same processor and the same Shiki themes as lesson Markdown, so a code block in a quiz looks like the one in the lesson above it.

They are also rendered with the same trust: raw HTML in a YAML string would reach the page, exactly as it would in a lesson .md. Both come from the repository.

Diagrams are drawn in the browser

A lesson writes a diagram as a ```mermaid block, and that block is the only copy. The fence is lifted out before Markdown rendering — Shiki would otherwise highlight it as source code — and put back as a pre.mermaid, which MermaidDiagrams.astro draws once the page loads. Nothing is pre-rendered to an image, so a regenerated lesson cannot leave a stale picture behind, and a page with no diagram on it never loads the library.

The diagram is themed from the site's own CSS tokens rather than a packaged Mermaid theme, so it follows light and dark with everything else. Before the script runs, and if it never does, the block shows its own source.

The prose column is narrow, so each drawn diagram gets an Expand button that reopens it against the whole viewport in a <dialog>. It is labelled from the diagram's own accTitle, and closes on Escape, on the backdrop, or on the close button.

Quiz answers are in the page source

This is a static site, so correct answers, distractor explanations, hints and exercise solutions all ship to the browser. Anyone reading the HTML can see them. That is the accepted trade for having no server; it is fine for self-study and unfit for anything graded.

Progress

Lesson completion and quiz scores are kept in localStorage under st-course-progress/v1. Per browser, never sent anywhere, and clearable per course from the course page. Every read and write is guarded, so the site still works with storage disabled — it just stops remembering.

Layout

src/
  assets/logo.png       site mark, resized and served as webp by astro:assets
  lib/
    courses.ts          the only module that touches disk; never throws, collects warnings
    schema.ts           lenient zod schemas mirroring the pipeline's YAML
    markdown.ts         renders the Markdown held in YAML prose fields
    shiki.ts            highlighting config, shared with astro.config.ts
    paths.ts            courses/ location, injected at build time
    watch-courses.ts    dev-only watcher
    format.ts
  content.config.ts     lesson Markdown as an Astro content collection
  components/           quiz, exercises, progress, badges, mermaid diagrams
  layouts/BaseLayout.astro
  pages/
    index.astro                              the library
    courses/[course]/index.astro             course overview
    courses/[course]/[module]/[lesson].astro lesson
    courses/[course]/glossary.astro
    courses/[course]/project.astro
    404.astro
  scripts/progress.ts   localStorage
  styles/global.css
astro.config.ts

courses.ts is deliberately the only file that reads the filesystem, and it is deliberately forgiving: a course with a malformed project.yaml loses its project page rather than breaking the build. Malformed artefacts are recorded as warnings, and the pipeline's own validator is what should be catching them.

Paths inside a course are resolved against the course directory and rejected if they escape it, so a path: in a manifest cannot be used to read arbitrary files.

Notes

  • The site is served from the domain root. To host it under a subpath, set base in astro.config.ts.
  • courses/ sits outside the project root, which is why vite.server.fs.allow includes ... That is a dev-server setting and has no effect on the built output.
  • Code blocks are highlighted by Shiki with a light and a dark theme, switched by prefers-color-scheme. There is no theme toggle.