PR-Dojo/dist/challenges/5/index.html

432 lines
34 KiB
HTML
Raw Normal View History

<!DOCTYPE html><html lang="en" class="dark" data-astro-cid-sckkx6r4> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><meta name="generator" content="Astro v6.1.7"><title>PR Dojo - Code Review Practice</title><link rel="stylesheet" href="/_astro/Layout.CjyFoliM.css">
<style>.left-gutter-row[data-astro-cid-2ex44dsa],.right-gutter-row[data-astro-cid-2ex44dsa]{height:1.6em}.code-row[data-astro-cid-2ex44dsa]{height:1.6em;min-height:1.6em}.editor-textarea[data-astro-cid-2ex44dsa]{line-height:1.6;tab-size:2;-moz-tab-size:2}.diff-row[data-astro-cid-2ex44dsa]{height:1.6em;min-height:1.6em}.diff-row[data-astro-cid-2ex44dsa].added{background-color:#3fb95018}.diff-row[data-astro-cid-2ex44dsa].removed,.code-row[data-astro-cid-2ex44dsa].diff-removed{background-color:#f8514918}.code-row[data-astro-cid-2ex44dsa].diff-added{background-color:#3fb95018}.diff-viewer-container[data-astro-cid-2ex44dsa]:not(.revealed) .has-hint[data-astro-cid-2ex44dsa] .code-text[data-astro-cid-2ex44dsa]{background-color:transparent!important}.diff-viewer-container[data-astro-cid-2ex44dsa].revealed .has-hint[data-astro-cid-2ex44dsa] .hint-text[data-astro-cid-2ex44dsa]{display:inline}.has-hint[data-astro-cid-2ex44dsa] .code-text[data-astro-cid-2ex44dsa],.has-hint[data-astro-cid-2ex44dsa].has-hint-bg .code-text[data-astro-cid-2ex44dsa]{background-color:#f8514918}.has-hint[data-astro-cid-2ex44dsa] .has-hint-num[data-astro-cid-2ex44dsa]{color:#f85149!important}.has-hint[data-astro-cid-2ex44dsa]:not(.revealed) .has-hint-num[data-astro-cid-2ex44dsa]{color:#484f58!important}.diff-viewer-container[data-astro-cid-2ex44dsa].revealed .has-hint[data-astro-cid-2ex44dsa] .has-hint-num[data-astro-cid-2ex44dsa]{color:#f85149!important}.overflow-auto[data-astro-cid-2ex44dsa]::-webkit-scrollbar{width:8px;height:8px}.overflow-auto[data-astro-cid-2ex44dsa]::-webkit-scrollbar-track{background:#0d1117}.overflow-auto[data-astro-cid-2ex44dsa]::-webkit-scrollbar-thumb{background:#30363d;border-radius:4px}.overflow-auto[data-astro-cid-2ex44dsa]::-webkit-scrollbar-thumb:hover{background:#484f58}#fix-editor-container[data-astro-cid-7fgtuneg] textarea[data-astro-cid-7fgtuneg]{resize:vertical;min-height:48px}
</style></head> <body data-astro-cid-sckkx6r4> <div class="min-h-screen" data-astro-cid-7fgtuneg> <!-- Header --> <header class="bg-[#161b22] border-b border-[#30363d]" data-astro-cid-7fgtuneg> <div class="max-w-6xl mx-auto px-4 py-3 flex items-center gap-4" data-astro-cid-7fgtuneg> <a href="/" class="text-[#58a6ff] text-sm font-medium no-underline focus:outline-none focus:ring-0" data-astro-cid-7fgtuneg>← Back to Challenges</a> <span class="text-[#8b949e] text-sm" data-astro-cid-7fgtuneg>/ Challenge #5</span> </div> </header> <main class="max-w-6xl mx-auto px-4 py-6" data-astro-cid-7fgtuneg> <!-- Challenge Header --> <div class="bg-[#161b22] border border-[#30363d] rounded-md p-6 mb-6" data-astro-cid-7fgtuneg> <div class="flex justify-between items-start mb-4" data-astro-cid-7fgtuneg> <div data-astro-cid-7fgtuneg> <h1 class="text-2xl font-semibold text-[#c9d1d9] mb-2 no-underline" data-astro-cid-7fgtuneg>Infinite Loop in Data Processing</h1> <div class="mb-4" data-astro-cid-7fgtuneg> <span class="text-[#79c0ff] text-xl no-underline" data-astro-cid-7fgtuneg>★★★☆☆</span> </div> <div class="flex flex-wrap items-center gap-4 text-sm" data-astro-cid-7fgtuneg> <span class="text-[#8b949e]" data-astro-cid-7fgtuneg>📁 <span class="text-[#a5d6ff]" data-astro-cid-7fgtuneg>angular/angular</span></span> <span class="text-[#8b949e]" data-astro-cid-7fgtuneg>🔢 <code class="bg-[#21262d] px-2 py-0.5 rounded text-xs" data-astro-cid-7fgtuneg>mno7890</code></span> <span class="px-2 py-0.5 bg-[#0a3064] rounded text-xs text-[#79c0ff]" data-astro-cid-7fgtuneg>Logic Error</span> </div> </div> <div class="text-right" data-astro-cid-7fgtuneg> <div class="text-[#79c0ff] text-lg mb-1 no-underline" data-astro-cid-7fgtuneg>★★★☆☆</div> <div class="text-xs text-[#8b949e]" data-astro-cid-7fgtuneg>Difficulty</div> </div> </div> <div class="bg-[#0a3064] rounded p-3 flex items-center gap-2" data-astro-cid-7fgtuneg> <svg class="w-5 h-5 text-[#58a6ff]" fill="currentColor" viewBox="0 0 20 20" data-astro-cid-7fgtuneg> <path d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z" data-astro-cid-7fgtuneg></path> </svg> <span class="text-[#79c0ff] font-semibold" data-astro-cid-7fgtuneg>+100 XP reward</span> </div> </div> <!-- Code Viewer --> <div id="dv-6n1i2of" class="diff-viewer-container" data-dv-id="dv-6n1i2of" data-original-code="function processItems(items) {
let index = 0;
while (index < items.length) {
console.log(items[index]);
// Missing: index++
}
return 'done';
}" data-file-extension="js" data-astro-cid-2ex44dsa> <div class="bg-[#0d1117] border border-[#30363d] rounded-md overflow-hidden" data-astro-cid-2ex44dsa> <!-- Toolbar --> <div class="bg-[#161b22] px-4 py-3 border-b border-[#30363d] flex items-center justify-between" data-astro-cid-2ex44dsa> <div class="flex items-center gap-3" data-astro-cid-2ex44dsa> <span class="text-sm text-[#8b949e]" data-astro-cid-2ex44dsa>DataProcessor.js.js</span> <span class="text-xs text-[#484f58]" data-astro-cid-2ex44dsa>|</span> <span class="text-xs text-[#8b949e]" data-astro-cid-2ex44dsa> <span id="left-lines-count" data-astro-cid-2ex44dsa>8</span> lines
</span> </div> <div class="flex items-center gap-2" data-astro-cid-2ex44dsa> <button id="reveal-hints-btn" class="text-xs px-2 py-1 rounded bg-[#21262d] text-[#8b949e] hover:text-[#c9d1d9] hover:bg-[#30363d] transition-colors focus:outline-none focus:ring-0" data-astro-cid-2ex44dsa>
Reveal Hints
</button> <button id="reset-btn" class="text-xs px-2 py-1 rounded bg-[#21262d] text-[#8b949e] hover:text-[#c9d1d9] hover:bg-[#30363d] transition-colors focus:outline-none focus:ring-0" data-astro-cid-2ex44dsa>
Reset
</button> </div> </div> <!-- Side-by-side viewer --> <div class="flex flex-col md:flex-row min-h-[400px]" data-astro-cid-2ex44dsa> <!-- Left panel: Original code --> <div class="flex-1 flex flex-col border-b md:border-b-0 md:border-r border-[#30363d]" data-astro-cid-2ex44dsa> <div class="bg-[#161b22] px-4 py-2 border-b border-[#30363d] flex items-center justify-between" data-astro-cid-2ex44dsa> <span class="text-xs font-semibold text-[#f85149] uppercase tracking-wider" data-astro-cid-2ex44dsa>Original (Buggy)</span> <span class="text-xs text-[#484f58]" data-astro-cid-2ex44dsa>Read-only</span> </div> <div class="flex-1 overflow-auto relative" data-astro-cid-2ex44dsa> <div class="flex min-h-full" data-astro-cid-2ex44dsa> <!-- Line numbers --> <div class="left-gutter select-none bg-[#0d1117] border-r border-[#30363d] py-2 sticky left-0 z-10" data-astro-cid-2ex44dsa> <div data-line="1" data-side="left" class="line-gutter-row font-mono text-right pr-3 pl-3 text-[#484f58] text-sm cursor-pointer hover:text-[#8b949e] transition-colors leading-[1.6] " title="Mark line 1 as buggy" data-astro-cid-2ex44dsa> 1 </div><div data-line="2" data-side="left" class="line-gutter-row font-mono text-right pr-3 pl-3 text-[#484f58] text-sm cursor-pointer hover:text-[#8b949e] transition-colors leading-[1.6] " title="Mark line 2 as buggy" data-astro-cid-2ex44dsa> 2 </div><div data-line="3" data-side="left" class="line-gutter-row font-mono text-right pr-3 pl-3 text-[#484f58] text-sm cursor-pointer hover:text-[#8b949e] transition-colors leading-[1.6] has-hint" title="Mark line 3 as buggy" data-astro-cid-2ex44dsa> 3 </div><div data-line="4" data-side="left" class="line-gutter-row font-mono text-right pr-3 pl-3 text-[#484f58] text-sm cursor-pointer hover:text-[#8b949e] transition-colors leading-[1.6] " title="Mark line 4 as buggy" data-astro-cid-2ex44dsa> 4 </div><div data-line="5" data-side="left" class="line-gutter-row font-mono text-right pr-3 pl-3 text-[#484f58] text-sm cursor-pointer hover:text-[#8b949e] transition-colors leading-[1.6] has-hint" title="Mark line 5 as buggy" data-astro-cid-2ex44dsa> 5 </div><div data-line="6" data-side="left" class="line-gutter-row font-mono text-right pr-3 pl-3 text-[#484f58] text-sm cursor-pointer hover:text-[#8b949e] transition-colors leading-[1.6] " title="Mark line 6 as buggy" data-astro-cid-2ex44dsa> 6 </div><div data-line="7" data-side="left" class="line-gutter-row font-mono text-right pr-3 pl-3 text-[#484f58] text-sm cursor-pointer hover:text-[#8b949e] transition-colors leading-[1.6] " title="Mark line 7 as buggy" data-astro-cid-2ex44dsa> 7 </div><div data-line="8" data-side="left" class="line-gutter-row font-mono text-right pr-3 pl-3 text-[#484f58] text-sm cursor-pointer hover:text-[#8b949e] transition-colors leading-[1.6] " title="Mark line 8 as buggy" data-astro-cid-2ex44dsa> 8 </div> </div> <!-- Code content --> <div class="left-code-area flex-1 py-2 overflow-hidden" data-astro-cid-2ex44dsa> <div data-line="1" data-side="left" class="code-row flex items-start gap-0 px-4 text-sm leading-[1.6] transition-colors " data-astro-cid-2ex44dsa> <span class="text-[#484f58] w-0 select-none " data-astro-cid-2ex44dsa></span> <span class="code-text font-mono text-[#e6edf3] whitespace-pre " data-astro-cid-2ex44dsa><span class="token keyword">function</span> <span class="token function">processItems</span><span class="token punctuation">(</span><span class="token parameter">items</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span> </div><div data-line="2" data-side="left" class="code-row flex items-start gap-0 px-4 text-sm leading-[1.6] transition-colors " data-astro-cid-2ex44dsa> <span class="text-[#484f58] w-0 select-none " data-astro-cid-2ex44dsa></span> <span class="code-text font-mono text-[#e6edf3] whitespace-pre " data-astro-cid-2ex44dsa> <span class="token keyword">let</span> index <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></span> </div><div data-line="3" data-side="left" class="code-row flex it
let index = 0;
while (index &lt; items.length) {
console.log(items[index]);
// Missing: index++
}
return &#39;done&#39;;
}</textarea> </div> </div> </div> </div> </div> <!-- Status bar --> <div class="bg-[#161b22] px-4 py-2 border-t border-[#30363d] flex items-center justify-between text-xs" data-astro-cid-2ex44dsa> <div class="flex items-center gap-4" data-astro-cid-2ex44dsa> <span class="text-[#484f58]" data-astro-cid-2ex44dsa> <span id="diff-added" class="text-[#3fb950]" data-astro-cid-2ex44dsa>0</span> added
</span> <span class="text-[#484f58]" data-astro-cid-2ex44dsa> <span id="diff-removed" class="text-[#f85149]" data-astro-cid-2ex44dsa>0</span> removed
</span> <span class="text-[#484f58]" data-astro-cid-2ex44dsa> <span id="diff-changed" class="text-[#d29922]" data-astro-cid-2ex44dsa>0</span> changed
</span> </div> <div class="text-[#484f58]" data-astro-cid-2ex44dsa>
Click lines on the left to mark as buggy
</div> </div> </div> </div> <script client:load>
(() => {
const container = document.querySelector('[data-dv-id]');
if (!container) return;
const originalCode = container.getAttribute('data-original-code') || '';
const originalLines = originalCode.split('\n');
const leftGutter = container.querySelector('.left-gutter');
const leftCodeArea = container.querySelector('.left-code-area');
const rightGutter = container.querySelector('.right-gutter');
const rightEditorArea = container.querySelector('.right-editor-area');
const textarea = document.getElementById('edit-textarea');
const diffHighlight = document.getElementById('diff-highlight');
const revealBtn = document.getElementById('reveal-hints-btn');
const resetBtn = document.getElementById('reset-btn');
const addedEl = document.getElementById('diff-added');
const removedEl = document.getElementById('diff-removed');
const changedEl = document.getElementById('diff-changed');
const buggedLines = new Set();
// Synchronized scrolling
function syncScroll(source, targetGutter, targetCode) {
if (targetCode) {
targetCode.scrollTop = source.scrollTop;
targetCode.scrollLeft = source.scrollLeft;
}
if (targetGutter) {
targetGutter.scrollTop = source.scrollTop;
}
}
// Left side scroll sync (also scrolls right side)
if (leftCodeArea) {
leftCodeArea.addEventListener('scroll', () => {
syncScroll(leftCodeArea, rightGutter, rightEditorArea);
});
}
// Right side scroll sync (also scrolls left side)
if (rightEditorArea) {
rightEditorArea.addEventListener('scroll', () => {
syncScroll(rightEditorArea, leftGutter, leftCodeArea);
});
}
// Diff computation using LCS-based line diff
function computeDiff(oldText, newText) {
const oldLines = oldText.split('\n');
const newLines = newText.split('\n');
// LCS to compute diff
const m = oldLines.length;
const n = newLines.length;
const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (oldLines[i - 1] === newLines[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
// Backtrack to get diff ops
const ops = [];
let i = m, j = n;
while (i > 0 || j > 0) {
if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
ops.push({ type: 'equal', oldLine: i, newLine: j });
i--; j--;
} else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
ops.push({ type: 'added', newLine: j });
j--;
} else {
ops.push({ type: 'removed', oldLine: i });
i--;
}
}
ops.reverse();
return ops;
}
function updateDiffHighlight() {
if (!textarea || !diffHighlight) return;
const newText = textarea.value;
const allOld = originalLines;
const allNew = newText.split('\n');
const totalLines = Math.max(allOld.length, allNew.length);
let added = 0, removed = 0, changed = 0;
// Rebuild diff rows to match textarea line count
diffHighlight.innerHTML = '';
for (let i = 0; i < totalLines; i++) {
const row = document.createElement('div');
row.setAttribute('data-diff-line', String(i + 1));
row.className = 'diff-row h-[1.6em] px-4 transition-colors';
diffHighlight.appendChild(row);
}
// Compare line by line using LCS-based approach
let oi = 0, ni = 0;
const statusMap = new Map(); // old line index -> 'equal' | 'removed' | 'changed'
while (oi < allOld.length || ni < allNew.length) {
if (oi < allOld.length && ni < allNew.length && allOld[oi] === allNew[ni]) {
statusMap.set(oi, 'equal');
oi++;
ni++;
} else if (oi < allOld.length && ni < allNew.length) {
statusMap.set(oi, 'changed');
added++;
changed++;
oi++;
ni++;
} else if (oi < allOld.length) {
statusMap.set(oi, 'removed');
removed++;
oi++;
} else {
added++;
ni++;
}
}
// Apply to right side diff overlay
statusMap.forEach((status, idx) => {
const lineNum = idx + 1;
const row = diffHighlight.querySelector(`[data-diff-line="${lineNum}"]`);
if (row) {
if (status === 'removed') {
row.classList.add('removed');
} else if (status === 'changed') {
row.classList.add('added');
}
}
});
// Apply to left panel code rows (only removed = red, no yellow)
statusMap.forEach((status, idx) => {
const lineNum = idx + 1;
const row = leftCodeArea?.querySelector(`.code-row[data-line="${lineNum}"]`);
if (row) {
row.classList.remove('diff-removed', 'diff-added');
if (status === 'removed') {
row.classList.add('diff-removed');
}
}
});
// Highlight added lines beyond original length
for (let i = allOld.length; i < allNew.length; i++) {
const row = diffHighlight.querySelector(`[data-diff-line="${i + 1}"]`);
if (row) {
row.classList.add('added');
}
}
addedEl.textContent = added;
removedEl.textContent = removed;
changedEl.textContent = changed;
}
// Left panel: click to mark bugged lines (gutter + code area)
function toggleBuggedLine(lineNum) {
if (isNaN(lineNum) || lineNum === 0) return;
const gutterRow = leftGutter?.querySelector(`[data-line="${lineNum}"][data-side="left"]`);
const codeRow = leftCodeArea?.querySelector(`.code-row[data-line="${lineNum}"]`);
const isBugged = buggedLines.has(lineNum);
if (isBugged) {
buggedLines.delete(lineNum);
if (gutterRow) { gutterRow.style.backgroundColor = ''; gutterRow.style.borderLeft = ''; }
if (codeRow) { codeRow.style.backgroundColor = ''; }
} else {
buggedLines.add(lineNum);
if (gutterRow) { gutterRow.style.backgroundColor = '#f8514920'; gutterRow.style.borderLeft = '3px solid #f85149'; }
if (codeRow) { codeRow.style.backgroundColor = '#f8514920'; }
}
container.dispatchEvent(new CustomEvent('bugged-line-toggle', {
bubbles: true,
detail: { line: lineNum, bugged: buggedLines.has(lineNum), allBugged: [...buggedLines] }
}));
}
leftGutter?.addEventListener('click', (e) => {
const target = e.target;
if (!(target instanceof HTMLElement)) return;
const row = target.closest('[data-line][data-side="left"]');
if (!row) return;
const lineNum = parseInt(row.getAttribute('data-line') || '0', 10);
toggleBuggedLine(lineNum);
});
leftCodeArea?.addEventListener('click', (e) => {
const target = e.target;
if (!(target instanceof HTMLElement)) return;
const row = target.closest('.code-row');
if (!row) return;
const lineNum = parseInt(row.getAttribute('data-line') || '0', 10);
toggleBuggedLine(lineNum);
});
// Reveal hints
revealBtn?.addEventListener('click', () => {
const isRevealed = container.classList.toggle('revealed');
revealBtn.textContent = isRevealed ? 'Hide Hints' : 'Reveal Hints';
revealBtn.classList.toggle('text-[#79c0ff]', isRevealed);
});
// Reset
resetBtn?.addEventListener('click', () => {
textarea.value = originalLines.join('\n');
buggedLines.clear();
container.classList.remove('revealed');
revealBtn.textContent = 'Reveal Hints';
revealBtn.classList.remove('text-[#79c0ff]');
const gutterRows = leftGutter?.querySelectorAll('[data-side="left"]');
gutterRows?.forEach(row => {
row.style.backgroundColor = '';
row.style.borderLeft = '';
});
const codeRows = leftCodeArea?.querySelectorAll('.code-row');
codeRows?.forEach(row => {
row.style.backgroundColor = '';
row.classList.remove('diff-removed', 'diff-added');
});
updateDiffHighlight();
container.dispatchEvent(new CustomEvent('bugged-line-reset', {
bubbles: true,
detail: { allBugged: [] }
}));
});
// Textarea input -> update diff
textarea?.addEventListener('input', () => {
updateDiffHighlight();
});
// Tab key support in textarea
textarea?.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
e.preventDefault();
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
textarea.value = textarea.value.substring(0, start) + ' ' + textarea.value.substring(end);
textarea.selectionStart = textarea.selectionEnd = start + 2;
updateDiffHighlight();
}
});
// Initial diff
updateDiffHighlight();
})();
</script> <!-- Fix Submission --> <div id="fix-panel" class="mt-6 bg-[#161b22] border border-[#30363d] rounded-md overflow-hidden" data-astro-cid-7fgtuneg> <div class="bg-[#161b22] px-4 py-3 border-b border-[#30363d] flex items-center justify-between" data-astro-cid-7fgtuneg> <h2 class="text-lg font-semibold text-[#c9d1d9]" data-astro-cid-7fgtuneg>Submit Your Fix</h2> <div class="text-sm text-[#8b949e]" data-astro-cid-7fgtuneg>
Lines marked: <span id="bugged-count-num" class="text-[#f85149] font-semibold" data-astro-cid-7fgtuneg>0</span> </div> </div> <div class="p-4" data-astro-cid-7fgtuneg> <div class="mb-4" data-astro-cid-7fgtuneg> <p class="text-sm text-[#8b949e] mb-1" data-astro-cid-7fgtuneg>
Edit the code on the <span class="text-[#3fb950] font-medium" data-astro-cid-7fgtuneg>right panel</span> above to fix the bugs.
Changes are highlighted in real-time.
</p> <p class="text-xs text-[#484f58]" data-astro-cid-7fgtuneg> <span class="text-[#f85149]" data-astro-cid-7fgtuneg></span> Removed &nbsp;
<span class="text-[#d29922]" data-astro-cid-7fgtuneg></span> Changed &nbsp;
<span class="text-[#3fb950]" data-astro-cid-7fgtuneg></span> Added &nbsp;
&nbsp;Click lines on the left to mark buggy lines
</p> </div> <!-- Bugged lines summary --> <div id="marked-lines-summary" class="mb-4 hidden" data-astro-cid-7fgtuneg> <p class="text-sm font-medium text-[#c9d1d9] mb-2" data-astro-cid-7fgtuneg>Marked buggy lines:</p> <div id="marked-lines-tags" class="flex flex-wrap gap-2" data-astro-cid-7fgtuneg></div> </div> <div class="flex gap-3" data-astro-cid-7fgtuneg> <button id="submit-fix-btn" class="flex-1 bg-[#1f6feb] hover:bg-[#388bfd] disabled:bg-[#21262d] disabled:text-[#484f58] disabled:cursor-not-allowed text-white font-semibold py-2.5 px-4 rounded-md transition-colors focus:outline-none focus:ring-0" data-astro-cid-7fgtuneg>
Submit Fix
</button> <button id="clear-all-btn" class="bg-[#21262d] hover:bg-[#30363d] text-[#8b949e] font-semibold py-2.5 px-4 rounded-md transition-colors focus:outline-none focus:ring-0" data-astro-cid-7fgtuneg>
Clear All
</button> </div> <div id="submission-message" class="mt-3 text-sm hidden" data-astro-cid-7fgtuneg></div> </div> </div> </main> </div> <footer class="border-t border-[#30363d] mt-16"> <div class="max-w-6xl mx-auto px-4 py-12"> <div class="grid grid-cols-2 md:grid-cols-4 gap-8"> <div> <h3 class="text-[#c9d1d9] font-semibold mb-4">PR Dojo</h3> <ul class="space-y-2"> <li> <a href="/" class="text-[#8b949e] text-sm hover:text-[#c9d1d9]">Challenges</a> </li> <li> <a href="/profile" class="text-[#8b949e] text-sm hover:text-[#c9d1d9]">Profile</a> </li> </ul> </div> <div> <h3 class="text-[#c9d1d9] font-semibold mb-4">Learn</h3> <ul class="space-y-2"> <li> <a href="/about" class="text-[#8b949e] text-sm hover:text-[#c9d1d9]">About</a> </li> <li> <a href="/faq" class="text-[#8b949e] text-sm hover:text-[#c9d1d9]">FAQ</a> </li> </ul> </div> <div> <h3 class="text-[#c9d1d9] font-semibold mb-4">Legal</h3> <ul class="space-y-2"> <li> <a href="/imprint" class="text-[#8b949e] text-sm hover:text-[#c9d1d9]">Imprint</a> </li> <li> <a href="/privacy" class="text-[#8b949e] text-sm hover:text-[#c9d1d9]">Privacy Policy</a> </li> <li> <a href="/terms" class="text-[#8b949e] text-sm hover:text-[#c9d1d9]">Terms of Service</a> </li> </ul> </div> <div> <h3 class="text-[#c9d1d9] font-semibold mb-4">Connect</h3> <ul class="space-y-2"> <li> <a href="https://github.com" target="_blank" rel="noopener noreferrer" class="text-[#8b949e] text-sm hover:text-[#c9d1d9]">GitHub</a> </li> <li> <a href="https://twitter.com" target="_blank" rel="noopener noreferrer" class="text-[#8b949e] text-sm hover:text-[#c9d1d9]">Twitter</a> </li> </ul> </div> </div> <div class="border-t border-[#30363d] mt-8 pt-8 text-center"> <p class="text-[#8b949e] text-sm">&copy; 2026 PR Dojo. All rights reserved.</p> </div> </div> </footer></body></html> <script client:load>
(() => {
const buggedLines = new Set();
const markedLinesTags = document.getElementById('marked-lines-tags');
const markedLinesSummary = document.getElementById('marked-lines-summary');
const buggedCountNum = document.getElementById('bugged-count-num');
const submitBtn = document.getElementById('submit-fix-btn');
const clearBtn = document.getElementById('clear-all-btn');
const submissionMessage = document.getElementById('submission-message');
function toRanges(sorted) {
const ranges = [];
if (sorted.length === 0) return ranges;
let start = sorted[0];
let end = sorted[0];
for (let i = 1; i < sorted.length; i++) {
if (sorted[i] === end + 1) {
end = sorted[i];
} else {
ranges.push([start, end]);
start = sorted[i];
end = sorted[i];
}
}
ranges.push([start, end]);
return ranges;
}
function updateUI() {
const arr = [...buggedLines].sort((a, b) => a - b);
buggedCountNum.textContent = arr.length.toString();
if (markedLinesTags) {
markedLinesTags.innerHTML = '';
const ranges = toRanges(arr);
ranges.forEach(([start, end]) => {
const tag = document.createElement('span');
tag.className = 'inline-flex items-center gap-1 bg-[#f8514920] text-[#f85149] text-xs px-2 py-0.5 rounded cursor-pointer hover:bg-[#f8514930] transition-colors';
tag.textContent = start === end ? `Line ${start}` : `Lines ${start}-${end}`;
markedLinesTags.appendChild(tag);
});
}
if (markedLinesSummary) {
markedLinesSummary.classList.toggle('hidden', arr.length === 0);
}
// Check if the editor has any changes
const textarea = document.getElementById('edit-textarea');
const editorChanged = textarea ? textarea.value !== textarea.defaultValue : false;
submitBtn.disabled = arr.length === 0 && !editorChanged;
}
// Listen for bugged-line-toggle events from DiffViewer
document.addEventListener('bugged-line-toggle', (e) => {
const detail = e.detail;
if (detail.bugged) {
buggedLines.add(detail.line);
} else {
buggedLines.delete(detail.line);
}
updateUI();
});
// Listen for bugged-line-reset events from DiffViewer
document.addEventListener('bugged-line-reset', (e) => {
buggedLines.clear();
updateUI();
});
// Listen for textarea changes
document.getElementById('edit-textarea')?.addEventListener('input', () => {
updateUI();
});
// Submit
if (submitBtn) {
submitBtn.addEventListener('click', () => {
const textarea = document.getElementById('edit-textarea');
const fixedCode = textarea?.value.trim() || '';
const originalCode = textarea?.defaultValue || '';
if (fixedCode === originalCode) {
if (submissionMessage) {
submissionMessage.textContent = 'Please make some changes to the code before submitting.';
submissionMessage.className = 'mt-3 text-sm text-[#f85149]';
submissionMessage.classList.remove('hidden');
}
return;
}
// Collect marked lines
const markedArr = [...buggedLines].sort((a, b) => a - b);
// Show success message
if (submissionMessage) {
const lineInfo = markedArr.length > 0
? ` Marked ${markedArr.length} buggy line(s).`
: ' No lines were specifically marked as buggy.';
submissionMessage.textContent = `Fix submitted!${lineInfo} (Backend integration coming soon)`;
submissionMessage.className = 'mt-3 text-sm text-[#3ac840]';
submissionMessage.classList.remove('hidden');
}
// Log the fix for debugging
console.log('=== Challenge Fix Submitted ===');
console.log('Marked buggy lines:', markedArr);
console.log('Fixed code:\n', fixedCode);
console.log('===============================');
});
}
// Clear all
if (clearBtn) {
clearBtn.addEventListener('click', () => {
buggedLines.clear();
if (submissionMessage) submissionMessage.classList.add('hidden');
updateUI();
// Trigger reset on DiffViewer
const resetBtn = document.getElementById('reset-btn');
if (resetBtn) resetBtn.click();
});
}
updateUI();
})();
</script>