---
name: "markdown-live-preview"
description: "Real-time markdown editor with split-pane live preview. Pure JavaScript, no external dependencies, dark theme. Type on the left, see rendered output on the right."
metadata:
  version: "1.0.0"
  dependencies:
    system:
      - "browser"
  runtime:
    language: "html"
    entrypoint: "preview.html"
    timeout_seconds: 30
---

See skill files — primary content is `preview.html`.

---

## Files

### `preview.html`

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Markdown Live Preview</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #0a0a1a; color: #e2e8f0; font-family: 'Segoe UI', system-ui, sans-serif; height: 100vh; display: flex; flex-direction: column; }
header { background: rgba(168,85,247,0.12); border-bottom: 1px solid rgba(168,85,247,0.25); padding: 10px 18px; display: flex; align-items: center; gap: 12px; }
header h1 { font-size: 15px; font-weight: 700; color: #c084fc; letter-spacing: 0.02em; }
.badge { background: rgba(168,85,247,0.2); border: 1px solid rgba(168,85,247,0.35); color: #a855f7; font-size: 10px; padding: 2px 8px; border-radius: 20px; font-family: monospace; }
.stats { margin-left: auto; font-size: 11px; color: #555; font-family: monospace; }
main { display: flex; flex: 1; overflow: hidden; }
.pane { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.pane-header { background: rgba(255,255,255,0.04); padding: 7px 16px; font-size: 11px; color: #666; border-bottom: 1px solid rgba(255,255,255,0.07); font-family: monospace; display: flex; align-items: center; gap: 8px; }
.pane-dot { width: 6px; height: 6px; border-radius: 50%; }
.divider { width: 1px; background: rgba(255,255,255,0.08); }
textarea { flex: 1; background: transparent; border: none; outline: none; padding: 18px 20px; color: #e2e8f0; font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace; font-size: 13px; line-height: 1.7; resize: none; }
#preview { flex: 1; padding: 18px 20px; overflow-y: auto; font-size: 14px; line-height: 1.8; }
#preview h1 { font-size: 22px; font-weight: 700; color: #f1f5f9; margin: 0 0 14px; padding-bottom: 8px; border-bottom: 1px solid rgba(168,85,247,0.2); }
#preview h2 { font-size: 17px; font-weight: 600; color: #e2e8f0; margin: 22px 0 10px; }
#preview h3 { font-size: 14px; font-weight: 600; color: #cbd5e1; margin: 18px 0 8px; }
#preview p { color: #94a3b8; margin: 0 0 10px; }
#preview a { color: #a855f7; text-decoration: none; border-bottom: 1px solid rgba(168,85,247,0.3); }
#preview strong { color: #e2e8f0; font-weight: 600; }
#preview em { color: #cbd5e1; font-style: italic; }
#preview code { background: rgba(168,85,247,0.12); border: 1px solid rgba(168,85,247,0.2); color: #c084fc; font-family: monospace; font-size: 12px; padding: 1px 6px; border-radius: 4px; }
#preview pre { background: rgba(0,0,0,0.5); border: 1px solid rgba(255,255,255,0.08); padding: 14px 16px; border-radius: 8px; margin: 0 0 14px; overflow-x: auto; }
#preview pre code { background: transparent; border: none; color: #e2e8f0; padding: 0; font-size: 12.5px; display: block; line-height: 1.6; }
#preview blockquote { border-left: 3px solid #a855f7; padding: 8px 14px; color: #94a3b8; background: rgba(168,85,247,0.06); margin: 0 0 14px; border-radius: 0 6px 6px 0; font-style: italic; }
#preview ul, #preview ol { padding-left: 22px; margin: 0 0 10px; }
#preview li { color: #94a3b8; margin-bottom: 3px; }
#preview hr { border: none; border-top: 1px solid rgba(255,255,255,0.08); margin: 20px 0; }
#preview table { width: 100%; border-collapse: collapse; margin: 0 0 14px; font-size: 13px; }
#preview th { background: rgba(168,85,247,0.15); color: #e2e8f0; padding: 7px 12px; text-align: left; font-size: 12px; border-bottom: 1px solid rgba(168,85,247,0.2); }
#preview td { padding: 7px 12px; border-bottom: 1px solid rgba(255,255,255,0.04); color: #94a3b8; }
#preview img { max-width: 100%; border-radius: 6px; }
::-webkit-scrollbar { width: 5px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: rgba(168,85,247,0.25); border-radius: 3px; }
</style>
</head>
<body>
<header>
  <h1>Markdown Live Preview</h1>
  <span class="badge">SkillSlap</span>
  <span class="stats" id="stats">0 chars · 0 words</span>
</header>
<main>
  <div class="pane">
    <div class="pane-header"><span class="pane-dot" style="background:#a855f7"></span>Markdown</div>
    <textarea id="editor" spellcheck="false"></textarea>
  </div>
  <div class="divider"></div>
  <div class="pane">
    <div class="pane-header"><span class="pane-dot" style="background:#10b981"></span>Preview</div>
    <div id="preview"></div>
  </div>
</main>
<script>
function md(s) {
  // Escape HTML first
  let o = s.replace(/&(?![a-zA-Z#]\w*;)/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  // Fenced code blocks
  o = o.replace(/```(\w*)\n?([\s\S]*?)```/g, (_,lang,code)=>`<pre><code class="lang-${lang}">${code.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>')}</code></pre>`);
  // Inline code
  o = o.replace(/`([^`\n]+)`/g,'<code>$1</code>');
  // Headers
  o = o.replace(/^#### (.+)$/gm,'<h4>$1</h4>').replace(/^### (.+)$/gm,'<h3>$1</h3>').replace(/^## (.+)$/gm,'<h2>$1</h2>').replace(/^# (.+)$/gm,'<h1>$1</h1>');
  // HR
  o = o.replace(/^---+$/gm,'<hr>');
  // Bold + italic
  o = o.replace(/\*\*\*(.+?)\*\*\*/g,'<strong><em>$1</em></strong>').replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>').replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,'<em>$1</em>');
  // Blockquotes
  o = o.replace(/^> (.+)$/gm,'<blockquote>$1</blockquote>');
  // Unordered lists
  o = o.replace(/((?:^[-*] .+\n?)+)/gm, m => '<ul>' + m.replace(/^[-*] (.+)$/gm,'<li>$1</li>') + '</ul>');
  // Ordered lists
  o = o.replace(/((?:^\d+\. .+\n?)+)/gm, m => '<ol>' + m.replace(/^\d+\. (.+)$/gm,'<li>$1</li>') + '</ol>');
  // Links
  o = o.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener">$1</a>');
  // Images
  o = o.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,'<img src="$2" alt="$1">');
  // Tables
  o = o.replace(/\|(.+)\|\n\|[-| :]+\|\n((?:\|.+\|\n?)+)/g, (_,h,rows)=>{
    const ths = h.split('|').filter(Boolean).map(c=>`<th>${c.trim()}</th>`).join('');
    const trs = rows.trim().split('\n').map(r=>'<tr>'+r.split('|').filter(Boolean).map(c=>`<td>${c.trim()}</td>`).join('')+'</tr>').join('');
    return `<table><thead><tr>${ths}</tr></thead><tbody>${trs}</tbody></table>`;
  });
  // Paragraphs (lines not starting with a tag)
  o = o.replace(/^(?!<[a-zA-Z\/])(.*\S.*)$/gm,'<p>$1</p>');
  return o;
}

const editor = document.getElementById('editor');
const preview = document.getElementById('preview');
const stats = document.getElementById('stats');

const DEFAULT = `# Markdown Live Preview

> Type in the **left pane** — see your rendered output instantly on the right.

## Features

- *Italic*, **bold**, \`inline code\`
- Fenced code blocks with syntax hints
- Tables, blockquotes, and horizontal rules
- Links, images, and nested lists

## Code Example

\`\`\`typescript
async function fetchSkill(id: string): Promise<Skill> {
  const { data, error } = await supabase
    .from('skills')
    .select('*')
    .eq('id', id)
    .single();
  if (error) throw error;
  return data;
}
\`\`\`

## Table

| Feature | Status |
|---------|--------|
| Headers | ✅ |
| Bold/Italic | ✅ |
| Code blocks | ✅ |
| Tables | ✅ |

---

Built with **pure JavaScript** — no external dependencies.`;

function update() {
  const val = editor.value;
  preview.innerHTML = md(val);
  const words = val.trim() ? val.trim().split(/\s+/).length : 0;
  stats.textContent = `${val.length} chars · ${words} words`;
}

editor.value = DEFAULT;
update();
editor.addEventListener('input', update);
</script>
</body>
</html>
```

### `README.md`

```md
# Markdown Live Preview

A real-time markdown editor and preview tool built for SkillSlap.

## Features
- Live preview as you type — no refresh needed
- Pure JavaScript markdown parser (no external deps)
- Supports: headers, bold/italic, code blocks, tables, blockquotes, lists, links, images
- Dark theme matching the SkillSlap design system
- Character and word count in the header

## Usage
Open in any browser. Type markdown in the left pane, see rendered HTML in the right pane.

```
