/** * markdown.ts — markdown → HTML, plus the HTML escaping used by the page builders. * * Answers and grade feedback are rendered here, at write time, and the HTML is * stored alongside the markdown. That keeps the browser runtime dependency-free. */ import { marked } from "marked"; /** Render markdown to HTML. Synchronous — `marked` async mode is not used. */ export function renderMarkdown(md: string): string { return marked.parse(md, { async: false, gfm: true, breaks: false }) as string; } /** Escape text for interpolation into an HTML text node or double-quoted attribute. */ export function escapeHtml(value: string): string { return value .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } /** * Serialize a value for embedding inside a ` sequence anywhere in the JSON (a lesson title, a learner's * question) would close the tag early. Escaping `<` handles that and `-->` in one go. */ export function toScriptJson(value: unknown): string { return JSON.stringify(value).replace(/