|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>{{TITLE}}</title> |
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css"> |
|
<link rel="preconnect" href="https://fonts.googleapis.com"> |
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Roboto:wght@400;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"> |
|
<style> |
|
:root { |
|
--bg: #242424; |
|
--bg-alt: #302e27; |
|
--bg-callout: #243f4040; |
|
--border: #524f47; |
|
--text: #fdfdfa; |
|
--text-muted: #9e9b94; |
|
--accent: #44bda3; |
|
--accent-dark: #3da892; |
|
--accent-light: #243f40; |
|
--green: #62d6a4; |
|
--green-bg: #1a3a2a; |
|
--orange: #e2a45f; |
|
--orange-bg: #3a2e1a; |
|
--red: #e0685e; |
|
--red-bg: #3a1a1a; |
|
--purple: #b392f0; |
|
--purple-bg: #2a1a3a; |
|
} |
|
* { box-sizing: border-box; margin: 0; padding: 0; } |
|
body { |
|
font-family: 'Roboto', sans-serif; |
|
color: var(--text); |
|
background: var(--bg); |
|
line-height: 1.7; |
|
font-size: 1.125rem; |
|
max-width: 860px; |
|
margin: 0 auto; |
|
padding: 2rem 1.5rem 4rem; |
|
position: relative; |
|
} |
|
body::before { |
|
content: ''; |
|
position: fixed; |
|
top: 0; left: 0; right: 0; bottom: 0; |
|
pointer-events: none; |
|
opacity: 0.04; |
|
background-image: url("data:image/svg+xml,%3Csvg width='4' height='4' viewBox='0 0 7 7' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2 0H0V2H2V0Z' fill='%23ffffff'/%3E%3C/svg%3E"); |
|
background-repeat: repeat; |
|
z-index: 0; |
|
} |
|
body > * { position: relative; z-index: 1; } |
|
h1 { font-family: 'Inter', sans-serif; font-size: 2.25rem; margin-bottom: 0.5rem; line-height: 1.2; font-weight: 600; letter-spacing: -0.02em; } |
|
h2 { font-family: 'Inter', sans-serif; font-size: 1.65rem; margin-top: 3rem; margin-bottom: 0.75rem; border-bottom: 1px solid var(--border); padding-bottom: 0.5rem; font-weight: 600; letter-spacing: -0.02em; } |
|
h3 { font-family: 'Inter', sans-serif; font-size: 1.25rem; margin-top: 1.75rem; margin-bottom: 0.5rem; font-weight: 600; letter-spacing: -0.02em; } |
|
h4 { font-family: 'Inter', sans-serif; font-size: 1.05rem; margin-top: 1.25rem; margin-bottom: 0.5rem; font-weight: 600; letter-spacing: -0.02em; } |
|
p { margin-bottom: 1.25rem; } |
|
a { color: var(--accent); text-decoration: none; font-weight: 500; } |
|
a:hover { color: var(--accent-dark); text-decoration: underline; } |
|
code { |
|
font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; |
|
font-size: 0.875em; |
|
background: color-mix(in srgb, var(--accent) 18%, var(--bg-alt)); |
|
border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent); |
|
padding: 0.15em 0.45em; |
|
border-radius: 4px; |
|
color: var(--accent); |
|
} |
|
pre { |
|
background: var(--bg); |
|
border: 1px solid color-mix(in srgb, var(--accent) 15%, transparent); |
|
border-radius: 6px; |
|
padding: 1.25rem; |
|
overflow-x: auto; |
|
margin: 1.75rem 0; |
|
} |
|
pre code { |
|
background: none; |
|
border: none; |
|
padding: 0; |
|
font-size: 0.85rem; |
|
line-height: 1.7; |
|
color: var(--text); |
|
white-space: pre; |
|
} |
|
.toc { |
|
background: var(--bg-alt); |
|
border: 1px solid var(--border); |
|
border-radius: 8px; |
|
padding: 0.75rem 1.25rem; |
|
margin-bottom: 2.5rem; |
|
} |
|
.toc h3 { margin-top: 0; margin-bottom: 0.4rem; font-size: 0.95rem; font-family: 'Inter', sans-serif; } |
|
.toc ol { padding-left: 1.5rem; } |
|
.toc li { margin-bottom: 0.15rem; font-size: 0.95rem; } |
|
.callout { |
|
border-left: 3px solid var(--accent); |
|
background: var(--bg-callout); |
|
padding: 0.75rem 1rem; |
|
border-radius: 0 6px 6px 0; |
|
margin-bottom: 1rem; |
|
} |
|
.callout.green { border-left-color: var(--green); background: var(--green-bg); } |
|
.callout.orange { border-left-color: var(--orange); background: var(--orange-bg); } |
|
.callout.red { border-left-color: var(--red); background: var(--red-bg); } |
|
.callout.purple { border-left-color: var(--purple); background: var(--purple-bg); } |
|
.callout strong { display: block; margin-bottom: 0.25rem; font-family: 'Inter', sans-serif; } |
|
|
|
/* Diagram styles */ |
|
.diagram { |
|
background: var(--bg-alt); |
|
border: 1px solid var(--border); |
|
border-radius: 8px; |
|
padding: 1.5rem; |
|
margin: 1.5rem 0; |
|
overflow-x: auto; |
|
} |
|
.diagram-title { |
|
font-size: 0.85rem; |
|
color: var(--text-muted); |
|
text-align: center; |
|
margin-bottom: 1rem; |
|
font-style: italic; |
|
} |
|
.figure-caption { |
|
font-size: 0.88rem; |
|
color: var(--text-muted); |
|
text-align: center; |
|
margin-top: 0.75rem; |
|
font-style: italic; |
|
} |
|
.flow-row { |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
gap: 0.5rem; |
|
margin-bottom: 0.75rem; |
|
flex-wrap: wrap; |
|
} |
|
.flow-box { |
|
padding: 0.5rem 0.75rem; |
|
border-radius: 6px; |
|
font-size: 0.82rem; |
|
font-weight: 600; |
|
text-align: center; |
|
min-width: 100px; |
|
white-space: nowrap; |
|
} |
|
.flow-box.blue { background: color-mix(in srgb, var(--accent) 15%, var(--bg-alt)); border: 1px solid var(--accent); color: var(--accent); } |
|
.flow-box.green { background: var(--green-bg); border: 1px solid var(--green); color: var(--green); } |
|
.flow-box.orange { background: var(--orange-bg); border: 1px solid var(--orange); color: var(--orange); } |
|
.flow-box.purple { background: var(--purple-bg); border: 1px solid var(--purple); color: var(--purple); } |
|
.flow-box.gray { background: #2a2a2a; border: 1px solid var(--border); color: var(--text-muted); } |
|
.flow-box.red { background: var(--red-bg); border: 1px solid var(--red); color: var(--red); } |
|
.flow-arrow { |
|
font-size: 1.2rem; |
|
color: var(--text-muted); |
|
flex-shrink: 0; |
|
} |
|
.flow-label { |
|
font-size: 0.75rem; |
|
color: var(--text-muted); |
|
text-align: center; |
|
margin-top: -0.25rem; |
|
margin-bottom: 0.5rem; |
|
} |
|
|
|
/* Option cards */ |
|
.option-card { |
|
border: 1px solid var(--border); |
|
border-radius: 8px; |
|
padding: 1.25rem; |
|
margin-bottom: 1.25rem; |
|
position: relative; |
|
background: var(--bg-alt); |
|
} |
|
.option-card h3 { margin-top: 0; } |
|
.option-card .verdict { |
|
font-size: 0.85rem; |
|
padding: 0.35rem 0.65rem; |
|
border-radius: 4px; |
|
display: inline-block; |
|
margin-top: 0.5rem; |
|
font-weight: 600; |
|
} |
|
.verdict.quick { background: var(--green-bg); color: var(--green); } |
|
.verdict.robust { background: color-mix(in srgb, var(--accent) 15%, var(--bg-alt)); color: var(--accent); } |
|
.verdict.best { background: var(--purple-bg); color: var(--purple); } |
|
.verdict.heavy { background: var(--orange-bg); color: var(--orange); } |
|
|
|
/* Glossary */ |
|
.glossary { |
|
background: var(--bg-alt); |
|
border: 1px solid var(--border); |
|
border-radius: 8px; |
|
padding: 1rem 1.25rem; |
|
margin: 1.5rem 0; |
|
} |
|
.glossary h3 { margin-top: 0; margin-bottom: 0.75rem; font-family: 'Inter', sans-serif; } |
|
.glossary-list { display: flex; flex-direction: column; gap: 0; } |
|
.glossary-item { |
|
font-size: 0.95rem; |
|
line-height: 1.6; |
|
padding: 0.5rem 0; |
|
border-bottom: 1px solid var(--border); |
|
} |
|
.glossary-item:last-child { border-bottom: none; } |
|
.glossary-term { font-weight: 600; margin-bottom: 0.15rem; color: var(--text); } |
|
.glossary-def { color: color-mix(in srgb, var(--text) 80%, transparent); } |
|
|
|
/* Quiz */ |
|
.quiz-q { |
|
border: 1px solid var(--border); |
|
border-radius: 8px; |
|
padding: 1.25rem; |
|
margin-bottom: 1.25rem; |
|
background: var(--bg-alt); |
|
} |
|
.quiz-q h4 { margin-bottom: 0.75rem; } |
|
.quiz-options { list-style: none; padding: 0; } |
|
.quiz-options li { |
|
padding: 0.5rem 0.75rem; |
|
margin-bottom: 0.4rem; |
|
border: 1px solid var(--border); |
|
border-radius: 6px; |
|
cursor: pointer; |
|
transition: background 0.15s, border-color 0.15s; |
|
font-size: 0.92rem; |
|
} |
|
.quiz-options li:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); } |
|
.quiz-options li.selected { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); } |
|
.quiz-options li.correct { border-color: var(--green); background: var(--green-bg); } |
|
.quiz-options li.wrong { border-color: var(--red); background: var(--red-bg); } |
|
.quiz-feedback { |
|
margin-top: 0.75rem; |
|
padding: 0.6rem 0.8rem; |
|
border-radius: 6px; |
|
font-size: 0.9rem; |
|
display: none; |
|
} |
|
.quiz-feedback.show { display: block; } |
|
.quiz-feedback.correct { background: var(--green-bg); border: 1px solid var(--green); } |
|
.quiz-feedback.wrong { background: var(--red-bg); border: 1px solid var(--red); } |
|
|
|
ul, ol { margin-bottom: 1rem; padding-left: 1.5rem; } |
|
li { margin-bottom: 0.3rem; } |
|
strong { font-weight: 600; } |
|
.subtitle { color: var(--text-muted); font-size: 1rem; margin-bottom: 2rem; } |
|
table { width: 100%; border-collapse: collapse; font-size: 0.88rem; margin-bottom: 2rem; } |
|
th, td { padding: 0.5rem; text-align: left; } |
|
thead { border-bottom: 2px solid var(--border); } |
|
tbody tr { border-bottom: 1px solid var(--border); } |
|
th { color: var(--text-muted); font-size: 0.8rem; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 500; } |
|
td { color: color-mix(in srgb, var(--text) 90%, transparent); } |
|
|
|
@media (max-width: 600px) { |
|
body { padding: 1rem; } |
|
h1 { font-size: 1.5rem; } |
|
h2 { font-size: 1.25rem; } |
|
.flow-box { font-size: 0.72rem; min-width: 70px; padding: 0.35rem 0.5rem; } |
|
pre { padding: 0.75rem; } |
|
} |
|
</style> |
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script> |
|
<script> |
|
document.addEventListener('DOMContentLoaded', () => { |
|
hljs.highlightAll(); |
|
|
|
// Parse feedback data from hidden elements |
|
const feedbackData = {}; |
|
document.querySelectorAll('.quiz-feedback-data').forEach(el => { |
|
const text = el.textContent.trim(); |
|
const questionBlocks = text.split(/(q\d+)/).filter(Boolean); |
|
|
|
for (let i = 0; i < questionBlocks.length; i += 2) { |
|
const qId = questionBlocks[i]; |
|
const feedbackText = questionBlocks[i + 1]?.trim() || ''; |
|
const [correct, wrong] = feedbackText.split(/\n\s*\n/).map(s => s.trim()).filter(Boolean); |
|
|
|
feedbackData[qId] = { |
|
correct: correct || 'Correct!', |
|
wrong: wrong || 'Not quite. Try again.' |
|
}; |
|
} |
|
}); |
|
|
|
document.querySelectorAll('.quiz-options').forEach(ul => { |
|
const qId = ul.closest('.quiz-q').id; |
|
const correctIdx = parseInt(ul.dataset.correct); |
|
const items = Array.from(ul.children); |
|
|
|
// Shuffle options |
|
for (let i = items.length - 1; i > 0; i--) { |
|
const j = Math.floor(Math.random() * (i + 1)); |
|
ul.appendChild(items[j]); |
|
items.splice(j, 1, items[i]); |
|
} |
|
|
|
ul.addEventListener('click', (e) => { |
|
const li = e.target.closest('li'); |
|
if (!li || ul.dataset.answered) return; |
|
ul.dataset.answered = 'true'; |
|
|
|
const chosenIdx = parseInt(li.dataset.idx); |
|
const isCorrect = chosenIdx === correctIdx; |
|
const fb = document.getElementById(qId + '-fb'); |
|
|
|
li.classList.add(isCorrect ? 'correct' : 'wrong'); |
|
if (!isCorrect) { |
|
items.find(i => parseInt(i.dataset.idx) === correctIdx).classList.add('correct'); |
|
} |
|
|
|
// Show feedback from parsed data |
|
const qFeedback = feedbackData[qId] || {}; |
|
if (qFeedback[isCorrect ? 'correct' : 'wrong']) { |
|
fb.textContent = qFeedback[isCorrect ? 'correct' : 'wrong']; |
|
} |
|
fb.className = 'quiz-feedback show ' + (isCorrect ? 'correct' : 'wrong'); |
|
}); |
|
}); |
|
}); |
|
</script> |
|
</head> |
|
<body> |
|
|
|
<!-- ════════════════════════════════════════════════════════════ |
|
HEADER — Replace placeholder text below |
|
════════════════════════════════════════════════════════════ --> |
|
<h1>{{TITLE}}</h1> |
|
<p class="subtitle">{{SUBTITLE — e.g. "A walkthrough of PR #123: Add rate limiting to the API gateway"}}</p> |
|
|
|
<!-- ════════════════════════════════════════════════════════════ |
|
TABLE OF CONTENTS — Update href IDs to match your h2s |
|
════════════════════════════════════════════════════════════ --> |
|
<div class="toc"> |
|
<h3>Contents</h3> |
|
<ol> |
|
<li><a href="#glossary">Glossary</a></li> |
|
<li><a href="#background">Background</a></li> |
|
<li><a href="#intuition">Intuition</a></li> |
|
<li><a href="#code">Code Walkthrough</a></li> |
|
<li><a href="#quiz">Quiz</a></li> |
|
</div> |
|
|
|
<!-- ════════════════════════════════════════════════════════════ |
|
SECTION 1: GLOSSARY |
|
════════════════════════════════════════════════════════════ --> |
|
<h2 id="glossary">1. Glossary</h2> |
|
|
|
<div class="glossary"> |
|
<h3>Key Terms</h3> |
|
<div class="glossary-list"> |
|
<div class="glossary-item"> |
|
<div class="glossary-term">{{Term}}</div> |
|
<div class="glossary-def">{{2-3 sentences: what it is, why it matters here, and optionally an analogy. Write for a junior developer who hasn't seen this term before.}}</div> |
|
</div> |
|
<div class="glossary-item"> |
|
<div class="glossary-term">{{Term}}</div> |
|
<div class="glossary-def">{{2-3 sentences explaining the term in plain language.}}</div> |
|
</div> |
|
<!-- Add more items as needed --> |
|
</div> |
|
</div> |
|
|
|
<!-- ════════════════════════════════════════════════════════════ |
|
SECTION 2: BACKGROUND |
|
════════════════════════════════════════════════════════════ --> |
|
<h2 id="background">2. Background</h2> |
|
|
|
<h3>The Big Picture</h3> |
|
|
|
<p>{{EXISTING SYSTEM — What does the code under review do today? How does it fit into the broader architecture?}}</p> |
|
|
|
<!-- ════════════════════════════════════════════════════════════ |
|
SECTION 3: INTUITION |
|
════════════════════════════════════════════════════════════ --> |
|
<h2 id="intuition">3. Intuition</h2> |
|
|
|
<p>{{CORE_INTUITION — What is the fundamental idea behind this change? Why does it work?}}</p> |
|
|
|
<!-- ── Example diagram: vertical flow (TD) ── --> |
|
<div class="diagram"> |
|
<div class="diagram-title">Fig 2. {{Caption explaining the example flow}}</div> |
|
<div class="flow-row"> |
|
<div class="flow-box blue">{{Step 1}}</div> |
|
</div> |
|
<div class="flow-row"> |
|
<span class="flow-arrow">↓</span> |
|
</div> |
|
<div class="flow-row"> |
|
<div class="flow-box green">{{Step 2}}</div> |
|
</div> |
|
<div class="flow-row"> |
|
<span class="flow-arrow">↓</span> |
|
</div> |
|
<div class="flow-row"> |
|
<div class="flow-box purple">{{Step 3}}</div> |
|
</div> |
|
</div> |
|
|
|
<p>{{EXPLANATION — Walk through the intuition step by step.}}</p> |
|
|
|
<div class="callout green"> |
|
<strong>Key Insight</strong> |
|
{{KEY_INSIGHT}} |
|
</div> |
|
|
|
<!-- ════════════════════════════════════════════════════════════ |
|
SECTION 3: CODE WALKTHROUGH |
|
════════════════════════════════════════════════════════════ --> |
|
<h2 id="code">4. Code Walkthrough</h2> |
|
|
|
<h3>File: {{filename}}</h3> |
|
|
|
<p>{{What changed and why}}</p> |
|
|
|
<pre><code class="language-{{language}}"> |
|
{{code snippet}} |
|
</code></pre> |
|
|
|
<p>{{Explanation of the code}}</p> |
|
|
|
<h3>File: {{filename 2}}</h3> |
|
|
|
<p>{{What changed and why}}</p> |
|
|
|
<pre><code class="language-{{language}}"> |
|
{{code snippet}} |
|
</code></pre> |
|
|
|
<p>{{Explanation of the code}}</p> |
|
|
|
<!-- ════════════════════════════════════════════════════════════ |
|
SECTION 4: QUIZ |
|
════════════════════════════════════════════════════════════ --> |
|
<h2 id="quiz">5. Quiz</h2> |
|
|
|
<p>Test your understanding. Options shuffle on each load.</p> |
|
|
|
<!-- ── Question 1 ── --> |
|
<div class="quiz-q" id="q1"> |
|
<h4>Q1. {{QUESTION TEXT}}</h4> |
|
<ul class="quiz-options" data-correct="{{0-3}}"> |
|
<li data-idx="0">{{OPTION A}}</li> |
|
<li data-idx="1">{{OPTION B}}</li> |
|
<li data-idx="2">{{OPTION C}}</li> |
|
<li data-idx="3">{{OPTION D}}</li> |
|
</ul> |
|
<div class="quiz-feedback" id="q1-fb"></div> |
|
</div> |
|
|
|
<!-- ── Question 2 ── --> |
|
<div class="quiz-q" id="q2"> |
|
<h4>Q2. {{QUESTION TEXT}}</h4> |
|
<ul class="quiz-options" data-correct="{{0-3}}"> |
|
<li data-idx="0">{{OPTION A}}</li> |
|
<li data-idx="1">{{OPTION B}}</li> |
|
<li data-idx="2">{{OPTION C}}</li> |
|
<li data-idx="3">{{OPTION D}}</li> |
|
</ul> |
|
<div class="quiz-feedback" id="q2-fb"></div> |
|
</div> |
|
|
|
<!-- ── Question 3 ── --> |
|
<div class="quiz-q" id="q3"> |
|
<h4>Q3. {{QUESTION TEXT}}</h4> |
|
<ul class="quiz-options" data-correct="{{0-3}}"> |
|
<li data-idx="0">{{OPTION A}}</li> |
|
<li data-idx="1">{{OPTION B}}</li> |
|
<li data-idx="2">{{OPTION C}}</li> |
|
<li data-idx="3">{{OPTION D}}</li> |
|
</ul> |
|
<div class="quiz-feedback" id="q3-fb"></div> |
|
</div> |
|
|
|
<!-- ── Question 4 ── --> |
|
<div class="quiz-q" id="q4"> |
|
<h4>Q4. {{QUESTION TEXT}}</h4> |
|
<ul class="quiz-options" data-correct="{{0-3}}"> |
|
<li data-idx="0">{{OPTION A}}</li> |
|
<li data-idx="1">{{OPTION B}}</li> |
|
<li data-idx="2">{{OPTION C}}</li> |
|
<li data-idx="3">{{OPTION D}}</li> |
|
</ul> |
|
<div class="quiz-feedback" id="q4-fb"></div> |
|
</div> |
|
|
|
<!-- ── Question 5 ── --> |
|
<div class="quiz-q" id="q5"> |
|
<h4>Q5. {{QUESTION TEXT}}</h4> |
|
<ul class="quiz-options" data-correct="{{0-3}}"> |
|
<li data-idx="0">{{OPTION A}}</li> |
|
<li data-idx="1">{{OPTION B}}</li> |
|
<li data-idx="2">{{OPTION C}}</li> |
|
<li data-idx="3">{{OPTION D}}</li> |
|
</ul> |
|
<div class="quiz-feedback" id="q5-fb"></div> |
|
</div> |
|
|
|
<!-- ════════════════════════════════════════════════════════════ |
|
QUIZ FEEDBACK DATA — Hidden, parsed by template JS |
|
Format: q1\nCorrect feedback\n\nWrong feedback\nq2... |
|
════════════════════════════════════════════════════════════ --> |
|
<div class="quiz-feedback-data" style="display: none;"> |
|
q1 |
|
{{CORRECT FEEDBACK}} |
|
|
|
{{WRONG FEEDBACK}} |
|
q2 |
|
{{CORRECT FEEDBACK}} |
|
|
|
{{WRONG FEEDBACK}} |
|
q3 |
|
{{CORRECT FEEDBACK}} |
|
|
|
{{WRONG FEEDBACK}} |
|
q4 |
|
{{CORRECT FEEDBACK}} |
|
|
|
{{WRONG FEEDBACK}} |
|
q5 |
|
{{CORRECT FEEDBACK}} |
|
|
|
{{WRONG FEEDBACK}} |
|
</div> |
|
|
|
</body> |
|
</html> |