import { useState, useMemo } from "react"; // ============================================= // TODO: Implement a Markdown-to-HTML parser // ============================================= // // Your parser should handle these markdown elements: // // Block-level: // - Headings: # H1, ## H2, ### H3 (up to ######) // - Code blocks: ```code``` (fenced with triple backticks) // - Blockquotes: > quoted text // - Unordered: - item or * item // - Ordered: 1. item // - Horizontal: --- (three or more dashes) // - Paragraphs: any other non-empty line // // Inline (within block text): // - Bold: **text** or __text__ // - Italic: *text* or _text_ // - Links: [text](url) // - Images:  // - Inline code: \`code\` // // Strategy: // 1. Handle fenced code blocks FIRST (before splitting lines) // — content inside must not be parsed by other rules // 2. Split remaining text into lines // 3. Iterate line by line, matching block patterns // 4. For lists/blockquotes, group consecutive matching lines // 5. Run inline parsing on text content within each block // // Hints: // - escapeHtml() for code block content (prevent <script> injection) // - Bold regex before italic (** is more specific than *) // - Images before links (![...] contains [...]) // - Use .replace() with regex for inline transforms // - Use a while loop to group consecutive list items // TODO: Implement escapeHtml // Replaces &, <, > with HTML entities function escapeHtml(str: string): string { // Your code here return str; } // TODO: Implement parseInline // Transforms inline markdown (bold, italic, links, images, code) function parseInline(text: string): string { // Your code here — apply regex replacements in order: // 1. Images  // 2. Links [text](url) // 3. Bold **text** // 4. Italic *text* // 5. Code \`code\` return text; } // TODO: Implement parseMarkdown // Converts a full markdown string to an HTML string function parseMarkdown(md: string): string { // Step 1: Handle fenced code blocks (``` ... ```) // - Use regex to match triple-backtick blocks // - Wrap content in <pre><code> // - escapeHtml() the content inside // Step 2: Split into lines and iterate // - For each line, check (in order): // a. Skip lines inside <pre> blocks // b. Horizontal rule: /^---+$/ // c. Heading: /^(#{1,6})\s+(.+)$/ // d. Blockquote: starts with "> " // e. Unordered list: starts with "- " or "* " // f. Ordered list: starts with "1. ", "2. ", etc. // g. Empty line: skip // h. Paragraph: wrap in <p> // - For lists and blockquotes, use a while loop to // collect consecutive matching lines // Step 3: Run parseInline() on text content within blocks return md; // ← Replace with your implementation } // ============================================= // Default markdown content (DO NOT MODIFY) // ============================================= const DEFAULT_MARKDOWN = `# Hello Markdown This is a **live preview** editor. Start typing on the left! ## Features - Bold and *italic* text - [Links](https://example.com) - Inline \`code\` snippets > Blockquotes look like this ### Code blocks \`\`\` function hello() { console.log("world"); } \`\`\` --- 1. First ordered item 2. Second ordered item 3. Third ordered item `; export default function App() { const [markdown, setMarkdown] = useState(DEFAULT_MARKDOWN); // TODO: Use useMemo to parse markdown only when it changes // const html = useMemo(() => parseMarkdown(markdown), [markdown]); const html = parseMarkdown(markdown); return ( <div style={{ maxWidth: 960, margin: "0 auto", padding: 24, fontFamily: "system-ui" }}> <h2 style={{ fontSize: 20, fontWeight: 700, marginBottom: 4 }}> Live Markdown Editor </h2> <p style={{ fontSize: 14, color: "#666", marginBottom: 20 }}> Write markdown on the left, see the rendered preview on the right. Implement <code>parseMarkdown</code>, <code>parseInline</code>, and <code> escapeHtml</code> at the top of this file. </p> {/* Supported syntax chips */} <div style={{ marginBottom: 20, padding: 14, background: "#f9fafb", border: "1px solid #e5e7eb", borderRadius: 8, }}> <p style={{ fontSize: 13, fontWeight: 600, marginBottom: 8, color: "#555" }}> Supported syntax: </p> <div style={{ display: "flex", flexWrap: "wrap", gap: 4 }}> {["# Heading", "**Bold**", "*Italic*", "[Link](url)", "`code`", "```block```", "> Quote", "- List", "1. Ordered", "---", "" ].map((s) => ( <span key={s} style={{ padding: "2px 8px", background: "#fff", border: "1px solid #e5e7eb", borderRadius: 4, fontSize: 11, color: "#666", fontFamily: "monospace", }} > {s} </span> ))} </div> </div> {/* Split pane */} <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}> {/* Editor */} <div> <label style={{ display: "block", fontSize: 14, fontWeight: 500, marginBottom: 8 }}> Markdown </label> <textarea value={markdown} onChange={(e) => setMarkdown(e.target.value)} placeholder="Write your markdown here..." spellCheck={false} style={{ width: "100%", height: 500, padding: "12px 14px", border: "1px solid #d1d5db", borderRadius: 8, fontSize: 13, fontFamily: "monospace", lineHeight: 1.6, resize: "none", outline: "none", boxSizing: "border-box", }} /> </div> {/* Preview */} <div> <label style={{ display: "block", fontSize: 14, fontWeight: 500, marginBottom: 8 }}> Preview </label> {/* ============================================= */} {/* TODO: Render the parsed HTML here */} {/* ============================================= */} {/* Use dangerouslySetInnerHTML={{ __html: html }} to render the parsed markdown as HTML. Note: dangerouslySetInnerHTML is safe here because the input comes from the user themselves (not from a database or another user). In production, you'd sanitize with DOMPurify. */} <div style={{ width: "100%", height: 500, padding: "12px 16px", border: "1px solid #e5e7eb", borderRadius: 8, overflowY: "auto", background: "#f9fafb", fontSize: 14, lineHeight: 1.7, boxSizing: "border-box", }} > {/* Replace this placeholder with dangerouslySetInnerHTML */} <p style={{ color: "#999", fontSize: 13 }}> Implement parseMarkdown() to see the preview here. </p> </div> </div> </div> {/* Hint */} <div style={{ marginTop: 24, padding: 16, background: "#f9fafb", borderLeft: "4px solid #111", borderRadius: "0 8px 8px 0", fontSize: 13, color: "#555", }}> <strong>Steps:</strong> (1) Implement <code>escapeHtml</code> to handle &, <, >. (2) Implement <code>parseInline</code> for bold, italic, links, images, code. (3) Implement <code>parseMarkdown</code> for block-level elements. (4) Wire up <code>dangerouslySetInnerHTML</code> on the preview div. (5) Wrap with <code>useMemo</code>. </div> </div> ); }