// ========================================== // Diff Viewer — Browser Extension // ========================================== // Renders ```diff code blocks with syntax-highlighted // additions, deletions, hunk headers, and context lines. // No external dependencies. // ========================================== Extensions.register({ id: 'diff-viewer', async init(ctx) { const self = this; // ── Inject styles ── this._injectStyles(); ctx.renderers.register('diff-block', { type: 'block', priority: 10, pattern: 'diff', render(lang, code, container) { const lines = code.split('\n'); let stats = { added: 0, removed: 0 }; let currentFile = ''; const rendered = lines.map(line => { const escaped = self._escapeHtml(line); // File headers if (line.startsWith('--- ') || line.startsWith('+++ ')) { if (line.startsWith('+++ ')) { currentFile = line.slice(4).trim(); } return `
${escaped}
`; } // Hunk headers if (line.startsWith('@@')) { const hunkMatch = line.match(/^@@\s*-(\d+)(?:,\d+)?\s*\+(\d+)(?:,\d+)?\s*@@\s*(.*)/); const context = hunkMatch ? hunkMatch[3] : ''; return `
${escaped}
`; } // Additions if (line.startsWith('+')) { stats.added++; return `
+${self._escapeHtml(line.slice(1))}
`; } // Deletions if (line.startsWith('-')) { stats.removed++; return `
-${self._escapeHtml(line.slice(1))}
`; } // Context (lines starting with space or no prefix) if (line.startsWith(' ')) { return `
${self._escapeHtml(line.slice(1))}
`; } // Other (commit info, etc) return `
${escaped}
`; }).join(''); const fileLabel = currentFile ? `${self._escapeHtml(currentFile)}` : ''; container.innerHTML = `
${fileLabel} +${stats.added} -${stats.removed}
${rendered}
📝 View raw
${self._escapeHtml(code)}
`; // Copy handler const copyBtn = container.querySelector('.diff-copy-btn'); if (copyBtn) { copyBtn.addEventListener('click', () => { navigator.clipboard.writeText(code).then(() => { copyBtn.textContent = '✓ Copied'; setTimeout(() => { copyBtn.textContent = '📋 Copy'; }, 1500); }).catch(() => {}); }); } } }); }, _escapeHtml(str) { const div = document.createElement('div'); div.textContent = str; return div.innerHTML; }, _injectStyles() { if (document.getElementById('ext-style-diff-viewer')) return; const style = document.createElement('style'); style.id = 'ext-style-diff-viewer'; style.textContent = ` .diff-block { background: var(--bg-2); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; margin: 12px 0; } .diff-toolbar { display: flex; align-items: center; gap: 8px; padding: 6px 12px; border-bottom: 1px solid var(--border); font-size: 12px; color: var(--text-3); } .diff-filename { flex: 1; font-family: var(--mono); font-weight: 600; color: var(--text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .diff-stats { display: flex; gap: 8px; font-family: var(--mono); font-weight: 600; } .diff-stat-add { color: var(--success, #27ae60); } .diff-stat-del { color: var(--error, #e74c3c); } .diff-copy-btn { background: none; border: 1px solid var(--border); border-radius: 4px; padding: 2px 8px; font-size: 12px; cursor: pointer; color: var(--text-3); } .diff-copy-btn:hover { color: var(--text-2); border-color: var(--text-3); } .diff-content { font-family: var(--mono); font-size: 13px; line-height: 1.5; overflow-x: auto; } .diff-line { padding: 1px 12px; white-space: pre; min-height: 1.5em; } .diff-indicator { display: inline-block; width: 1.2em; text-align: center; opacity: 0.7; user-select: none; } .diff-add { background: rgba(39,174,96,0.12); color: var(--success, #27ae60); } .diff-del { background: rgba(231,76,60,0.12); color: var(--error, #e74c3c); } .diff-hunk { background: rgba(52,152,219,0.08); color: var(--info, #3498db); font-style: italic; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); margin: 2px 0; } .diff-file-header { background: var(--bg-3); color: var(--text-2); font-weight: 600; } .diff-ctx { color: var(--text-3); } .diff-meta { color: var(--text-3); font-style: italic; } .diff-source-toggle { border-top: 1px solid var(--border); font-size: 12px; } .diff-source-toggle summary { padding: 6px 12px; cursor: pointer; color: var(--text-3); user-select: none; } .diff-source-toggle summary:hover { color: var(--text-2); } .diff-source-toggle pre { margin: 0; border-radius: 0; border: none; max-height: 200px; overflow: auto; }`; document.head.appendChild(style); }, destroy() { document.getElementById('ext-style-diff-viewer')?.remove(); } });