2026-05-03 15:36:14 +02:00
|
|
|
<!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 < items.length) {
|
|
|
|
|
console.log(items[index]);
|
|
|
|
|
// Missing: index++
|
|
|
|
|
}
|
|
|
|
|
return 'done';
|
|
|
|
|
}</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>
|
2026-04-17 13:09:06 +02:00
|
|
|
(() => {
|
2026-05-03 15:36:14 +02:00
|
|
|
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');
|
2026-04-17 13:09:06 +02:00
|
|
|
|
|
|
|
|
const buggedLines = new Set();
|
|
|
|
|
|
2026-05-03 15:36:14 +02:00
|
|
|
// 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);
|
2026-04-17 13:09:06 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-03 15:36:14 +02:00
|
|
|
// Right side scroll sync (also scrolls left side)
|
|
|
|
|
if (rightEditorArea) {
|
|
|
|
|
rightEditorArea.addEventListener('scroll', () => {
|
|
|
|
|
syncScroll(rightEditorArea, leftGutter, leftCodeArea);
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-04-17 13:09:06 +02:00
|
|
|
|
2026-05-03 15:36:14 +02:00
|
|
|
// 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) {
|
2026-04-17 13:09:06 +02:00
|
|
|
buggedLines.delete(lineNum);
|
2026-05-03 15:36:14 +02:00
|
|
|
if (gutterRow) { gutterRow.style.backgroundColor = ''; gutterRow.style.borderLeft = ''; }
|
|
|
|
|
if (codeRow) { codeRow.style.backgroundColor = ''; }
|
2026-04-17 13:09:06 +02:00
|
|
|
} else {
|
|
|
|
|
buggedLines.add(lineNum);
|
2026-05-03 15:36:14 +02:00
|
|
|
if (gutterRow) { gutterRow.style.backgroundColor = '#f8514920'; gutterRow.style.borderLeft = '3px solid #f85149'; }
|
|
|
|
|
if (codeRow) { codeRow.style.backgroundColor = '#f8514920'; }
|
2026-04-17 13:09:06 +02:00
|
|
|
}
|
|
|
|
|
|
2026-05-03 15:36:14 +02:00
|
|
|
container.dispatchEvent(new CustomEvent('bugged-line-toggle', {
|
2026-04-17 13:09:06 +02:00
|
|
|
bubbles: true,
|
|
|
|
|
detail: { line: lineNum, bugged: buggedLines.has(lineNum), allBugged: [...buggedLines] }
|
|
|
|
|
}));
|
2026-05-03 15:36:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2026-04-17 13:09:06 +02:00
|
|
|
});
|
2026-05-03 15:36:14 +02:00
|
|
|
|
|
|
|
|
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();
|
2026-04-17 13:09:06 +02:00
|
|
|
})();
|
2026-05-03 15:36:14 +02:00
|
|
|
</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
|
|
|
|
|
<span class="text-[#d29922]" data-astro-cid-7fgtuneg>■</span> Changed
|
|
|
|
|
<span class="text-[#3fb950]" data-astro-cid-7fgtuneg>■</span> Added
|
|
|
|
|
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>
|
2026-04-17 13:09:06 +02:00
|
|
|
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
|
2026-04-28 15:59:47 +02:00
|
|
|
</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">© 2026 PR Dojo. All rights reserved.</p> </div> </div> </footer></body></html> <script client:load>
|
2026-04-17 13:09:06 +02:00
|
|
|
(() => {
|
|
|
|
|
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');
|
2026-05-03 15:36:14 +02:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2026-04-17 13:09:06 +02:00
|
|
|
|
|
|
|
|
function updateUI() {
|
|
|
|
|
const arr = [...buggedLines].sort((a, b) => a - b);
|
|
|
|
|
buggedCountNum.textContent = arr.length.toString();
|
|
|
|
|
|
|
|
|
|
if (markedLinesTags) {
|
|
|
|
|
markedLinesTags.innerHTML = '';
|
2026-05-03 15:36:14 +02:00
|
|
|
const ranges = toRanges(arr);
|
|
|
|
|
ranges.forEach(([start, end]) => {
|
2026-04-17 13:09:06 +02:00
|
|
|
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';
|
2026-05-03 15:36:14 +02:00
|
|
|
tag.textContent = start === end ? `Line ${start}` : `Lines ${start}-${end}`;
|
2026-04-17 13:09:06 +02:00
|
|
|
markedLinesTags.appendChild(tag);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (markedLinesSummary) {
|
|
|
|
|
markedLinesSummary.classList.toggle('hidden', arr.length === 0);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-03 15:36:14 +02:00
|
|
|
// 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;
|
2026-04-17 13:09:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
});
|
|
|
|
|
|
2026-05-03 15:36:14 +02:00
|
|
|
// 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();
|
|
|
|
|
});
|
2026-04-17 13:09:06 +02:00
|
|
|
|
|
|
|
|
// Submit
|
|
|
|
|
if (submitBtn) {
|
|
|
|
|
submitBtn.addEventListener('click', () => {
|
2026-05-03 15:36:14 +02:00
|
|
|
const textarea = document.getElementById('edit-textarea');
|
|
|
|
|
const fixedCode = textarea?.value.trim() || '';
|
|
|
|
|
const originalCode = textarea?.defaultValue || '';
|
2026-04-17 13:09:06 +02:00
|
|
|
|
2026-05-03 15:36:14 +02:00
|
|
|
if (fixedCode === originalCode) {
|
2026-04-17 13:09:06 +02:00
|
|
|
if (submissionMessage) {
|
2026-05-03 15:36:14 +02:00
|
|
|
submissionMessage.textContent = 'Please make some changes to the code before submitting.';
|
2026-04-17 13:09:06 +02:00
|
|
|
submissionMessage.className = 'mt-3 text-sm text-[#f85149]';
|
|
|
|
|
submissionMessage.classList.remove('hidden');
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-03 15:36:14 +02:00
|
|
|
// Collect marked lines
|
|
|
|
|
const markedArr = [...buggedLines].sort((a, b) => a - b);
|
2026-04-28 15:59:47 +02:00
|
|
|
|
2026-04-17 13:09:06 +02:00
|
|
|
// Show success message
|
|
|
|
|
if (submissionMessage) {
|
2026-05-03 15:36:14 +02:00
|
|
|
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)`;
|
2026-04-28 15:59:47 +02:00
|
|
|
submissionMessage.className = 'mt-3 text-sm text-[#3ac840]';
|
2026-04-17 13:09:06 +02:00
|
|
|
submissionMessage.classList.remove('hidden');
|
|
|
|
|
}
|
2026-05-03 15:36:14 +02:00
|
|
|
|
|
|
|
|
// Log the fix for debugging
|
|
|
|
|
console.log('=== Challenge Fix Submitted ===');
|
|
|
|
|
console.log('Marked buggy lines:', markedArr);
|
|
|
|
|
console.log('Fixed code:\n', fixedCode);
|
|
|
|
|
console.log('===============================');
|
2026-04-17 13:09:06 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear all
|
|
|
|
|
if (clearBtn) {
|
|
|
|
|
clearBtn.addEventListener('click', () => {
|
|
|
|
|
buggedLines.clear();
|
|
|
|
|
if (submissionMessage) submissionMessage.classList.add('hidden');
|
|
|
|
|
updateUI();
|
|
|
|
|
|
2026-05-03 15:36:14 +02:00
|
|
|
// Trigger reset on DiffViewer
|
|
|
|
|
const resetBtn = document.getElementById('reset-btn');
|
|
|
|
|
if (resetBtn) resetBtn.click();
|
2026-04-17 13:09:06 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateUI();
|
|
|
|
|
})();
|
|
|
|
|
</script>
|