Files
awesome-reviewers/_layouts/default.html
2025-07-30 10:36:52 +03:00

1007 lines
43 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="index, follow">
<meta name="author" content="Baz Technologies">
<meta property="og:title" content="{% if page.title %}{{ page.title }} - {% endif %}{{ site.title }}">
<meta property="og:description" content="{{ page.description | default: site.description }}">
<meta property="og:url" content="{{ site.url }}{{ page.url }}">
<meta property="og:image" content="{{ site.url }}/assets/images/ar-web.png">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Awesome Reviewers">
<!-- Twitter Cards -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@baz_scm">
<meta name="twitter:title" content="{% if page.title %}{{ page.title }} - {% endif %}{{ site.title }}">
<meta name="twitter:description" content="{{ page.description | default: site.description }}">
<meta name="twitter:image" content="{{ site.url }}/assets/images/ar-web.png">
<title>{% if page.title %}{{ page.title }} - {% endif %}{{ site.title }}</title>
<meta name="description" content="{{ page.description | default: site.description }}">
<link rel="icon" type="image/x-icon" href="{{ '/favicon.png' | relative_url }}">
<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&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{{ '/assets/css/main.css' | relative_url }}">
<link rel="stylesheet" href="{{ '/assets/prism/prism.css' | relative_url }}">
<link rel="manifest" href="{{ '/manifest.json' | relative_url }}">
<link rel="canonical" href="{{ site.url }}{{ page.url }}">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-2S2LLCMG7N"></script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "Awesome Reviewers",
"applicationCategory": "DeveloperApplication",
"operatingSystem": "Web",
"description": "AI-powered code review system prompts and templates",
"url": "https://awesomereviewers.com",
"author": {
"@type": "Organization",
"name": "Baz Technologies",
"url": "https://baz.co"
},
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
}
}
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-2S2LLCMG7N');
</script>
<script>
!function () {
var reb2b = window.reb2b = window.reb2b || [];
if (reb2b.invoked) return;
reb2b.invoked = true;
reb2b.methods = ["identify", "collect"];
reb2b.factory = function(method) {
return function() {
var args = Array.prototype.slice.call(arguments);
args.unshift(method);
reb2b.push(args);
return reb2b;
};
};
for (var i = 0; i < reb2b.methods.length; i++) {
var key = reb2b.methods[i];
reb2b[key] = reb2b.factory(key);
}
reb2b.load = function(key) {
var script = document.createElement("script");
script.type = "text/javascript";
script.async = true;
script.src = "https://b2bjsstore.s3.us-west-2.amazonaws.com/b/" + key + "/4O7Z0HJL9PNX.js.gz";
var first = document.getElementsByTagName("script")[0];
first.parentNode.insertBefore(script, first);
};
reb2b.SNIPPET_VERSION = "1.0.1";
reb2b.load("4O7Z0HJL9PNX");
}();
</script>
</head>
<body>
<header class="header">
<div class="container">
<div class="header-content">
<a href="https://baz.co" class="logo" target="_blank" rel="noopener noreferrer" aria-label="Visit Baz Technologies homepage">
<div class="logo-icon">
<img src="{{ '/assets/images/baz-light.svg' | relative_url }}" alt="Baz Technologies Logo" class="logo-light" width="80" height="80">
<img src="{{ '/assets/images/baz-dark.svg' | relative_url }}" alt="Baz Technologies Logo" class="logo-dark" width="80" height="80"
style="display: none;">
</div>
</a>
<button class="menu-toggle" aria-label="Toggle navigation">
<svg class="hamburger-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<path d="M3 6h18M3 12h18M3 18h18"/>
</svg>
<svg class="close-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" style="display: none;">
<path d="M6 6l12 12M6 18L18 6"/>
</svg>
</button>
<div class="cta-group">
<div class="header-links">
<a href="/leaderboard" class="header-link secondary" aria-label="View contributor leaderboard">Leaderboard</a>
<a href="https://baz.co/agents" class="header-link primary" target="_blank"
rel="noopener noreferrer" aria-label="Deploy AI agents with Baz">Deploy agents</a>
</div>
<div class="header-social">
<a href="https://github.com/baz-scm/awesome-reviewers" target="_blank" rel="noopener noreferrer" title="GitHub" aria-label="GitHub repository">
<svg width="20" height="20" viewBox="0 0 16 16" fill="currentColor">
<path d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"/>
</svg>
</a>
<a href="https://x.com/baz_scm" target="_blank" rel="noopener noreferrer" title="Follow us on X" aria-label="Follow us on X (formerly Twitter)">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
</svg>
</a>
</div>
</div>
</div>
</div>
</div>
</header>
{{ content }}
<footer class="footer">
<div class="container">
<div class="footer-links">
<a href="https://baz.co/resources/from-review-thread-to-team-standard-how-we-built-awesomereviewers" class="header-link secondary" target="_blank" rel="noopener noreferrer" aria-label="Learn more about how we built Awesome Reviewers">Blog post</a>
<a href="/leaderboard" class="header-link secondary" aria-label="View contributor leaderboard">Leaderboard</a>
<a href="https://docs.baz.co/basics/model-context-protocol-mcp" class="header-link secondary" target="_blank" rel="noopener noreferrer" aria-label="Learn about Model Context Protocol (MCP)">MCP</a>
<a href="https://baz.co/agents" class="header-link secondary" target="_blank" rel="noopener noreferrer" aria-label="Deploy AI agents with Baz">Deploy agents</a>
<a href="/privacy" class="header-link secondary" aria-label="View privacy policy">Privacy</a>
<div class="theme-switch-container">
<div class="theme-switch">
<button class="theme-option light" onclick="setTheme('light')" aria-label="Switch to light theme">
<svg width="16" height="16" viewBox="0 0 16 16" class="theme-icon" aria-hidden="true">
<path fill-rule="evenodd"
d="M8 12a4 4 0 100-8 4 4 0 000 8zM8 0a.5.5 0 01.5.5v2a.5.5 0 01-1 0v-2A.5.5 0 018 0zm0 13a.5.5 0 01.5.5v2a.5.5 0 01-1 0v-2A.5.5 0 018 13zm8-5a.5.5 0 01-.5.5h-2a.5.5 0 010-1h2a.5.5 0 01.5.5zM3 8a.5.5 0 01-.5.5h-2a.5.5 0 010-1h2A.5.5 0 013 8zm10.657-5.657a.5.5 0 010 .707l-1.414 1.415a.5.5 0 11-.707-.708l1.414-1.414a.5.5 0 01.707 0zm-9.193 9.193a.5.5 0 010 .707L3.05 13.657a.5.5 0 01-.707-.707l1.414-1.414a.5.5 0 01.707 0zm9.193 0a.5.5 0 01-.707 0L11.536 13.95a.5.5 0 01.707.707l1.414-1.414a.5.5 0 010-.707zM4.464 4.465a.5.5 0 01-.707 0L2.343 3.05a.5.5 0 11.707-.707l1.414 1.414a.5.5 0 010 .708z"/>
</svg>
<span>Light</span>
</button>
<button class="theme-option dark" onclick="setTheme('dark')" aria-label="Switch to dark theme">
<svg width="16" height="16" viewBox="0 0 16 16" class="theme-icon" aria-hidden="true">
<path d="M6 .278a.768.768 0 01.08.858 7.208 7.208 0 00-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 01.81.316.733.733 0 01-.031.893A8.349 8.349 0 018.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 016 .278z"/>
</svg>
<span>Dark</span>
</button>
</div>
</div>
</div>
<p>&copy; <span id="current-year"> </span> Baz Technologies. Ready-to-use system prompts for better code reviews.</p>
</div>
</footer>
<script>
// Theme switching functionality
function setTheme(theme) {
const html = document.documentElement;
const logoLight = document.querySelector('.logo-light');
const logoDark = document.querySelector('.logo-dark');
const lightButton = document.querySelector('.theme-option.light');
const darkButton = document.querySelector('.theme-option.dark');
// Remove active class from both buttons
lightButton.classList.remove('active');
darkButton.classList.remove('active');
if (theme === 'dark') {
html.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
logoLight.style.display = 'none';
logoDark.style.display = 'block';
darkButton.classList.add('active');
} else {
html.removeAttribute('data-theme');
localStorage.setItem('theme', 'light');
logoLight.style.display = 'block';
logoDark.style.display = 'none';
lightButton.classList.add('active');
}
}
// Load saved theme
document.addEventListener('DOMContentLoaded', function () {
const savedTheme = localStorage.getItem('theme') || 'light';
const logoLight = document.querySelector('.logo-light');
const logoDark = document.querySelector('.logo-dark');
const lightButton = document.querySelector('.theme-option.light');
const darkButton = document.querySelector('.theme-option.dark');
document.getElementById('current-year').textContent = new Date().getFullYear().toString();
if (savedTheme === 'dark') {
document.documentElement.setAttribute('data-theme', 'dark');
logoLight.style.display = 'none';
logoDark.style.display = 'block';
darkButton.classList.add('active');
} else {
lightButton.classList.add('active');
}
});
// Multi-select filter functionality
class MultiSelect {
constructor(containerId, options) {
this.container = document.getElementById(containerId);
this.options = options;
this.selectedValues = [];
this.filteredOptions = [...options];
this.init();
}
init() {
this.render();
this.attachEvents();
}
render() {
const selectedText = this.selectedValues.length === 0
? this.container.dataset.placeholder
: this.selectedValues.length === 1
? this.selectedValues[0]
: `${this.selectedValues.length} selected`;
// Sort options: selected items first, then unselected items
const sortedOptions = this.getSortedOptions();
this.container.innerHTML = `
<div class="multi-select-trigger">
<div class="multi-select-content">
<span style="color: ${this.selectedValues.length === 0 ? 'var(--text-muted)' : 'var(--text-primary)'}">${selectedText}</span>
</div>
<div class="multi-select-arrow">▼</div>
</div>
<div class="multi-select-dropdown">
<input type="text" class="multi-select-search" placeholder="Search...">
<div class="multi-select-options">
${sortedOptions.map(option =>
`<div class="multi-select-option">
<input type="checkbox" value="${option}" ${this.selectedValues.includes(option) ? 'checked' : ''}>
<span>${option}</span>
</div>`
).join('')}
</div>
</div>
`;
}
getSortedOptions() {
const selected = this.filteredOptions.filter(option => this.selectedValues.includes(option));
const unselected = this.filteredOptions.filter(option => !this.selectedValues.includes(option));
return [...selected, ...unselected];
}
attachEvents() {
const trigger = this.container.querySelector('.multi-select-trigger');
const searchInput = this.container.querySelector('.multi-select-search');
trigger.addEventListener('click', (e) => {
e.stopPropagation();
this.toggleDropdown();
});
searchInput.addEventListener('input', (e) => {
this.filterOptions(e.target.value);
});
this.attachOptionEvents();
document.addEventListener('click', () => {
this.closeDropdown();
});
}
attachOptionEvents() {
const dropdown = this.container.querySelector('.multi-select-dropdown');
if (!dropdown) return;
dropdown.addEventListener('click', (e) => {
e.stopPropagation();
if (e.target.type === 'checkbox') {
this.toggleOption(e.target.value, e.target.checked);
} else if (e.target.closest('.multi-select-option')) {
// Clicked anywhere on the option line
const option = e.target.closest('.multi-select-option');
const checkbox = option.querySelector('input[type="checkbox"]');
if (checkbox) {
checkbox.checked = !checkbox.checked;
this.toggleOption(checkbox.value, checkbox.checked);
}
}
});
}
toggleDropdown() {
this.container.classList.toggle('open');
const dropdown = this.container.querySelector('.multi-select-dropdown');
dropdown.classList.toggle('open');
if (dropdown.classList.contains('open')) {
const searchInput = this.container.querySelector('.multi-select-search');
searchInput.focus();
}
}
closeDropdown() {
this.container.classList.remove('open');
const dropdown = this.container.querySelector('.multi-select-dropdown');
dropdown.classList.remove('open');
}
filterOptions(searchTerm) {
this.filteredOptions = this.options.filter(option =>
option.toLowerCase().includes(searchTerm.toLowerCase())
);
this.updateOptions();
}
updateOptions() {
const optionsContainer = this.container.querySelector('.multi-select-options');
const sortedOptions = this.getSortedOptions();
optionsContainer.innerHTML = sortedOptions.map(option =>
`<div class="multi-select-option">
<input type="checkbox" value="${option}" ${this.selectedValues.includes(option) ? 'checked' : ''}>
<span>${option}</span>
</div>`
).join('');
// Re-attach events for new options
this.attachOptionEvents();
}
toggleOption(value, checked) {
if (checked && !this.selectedValues.includes(value)) {
this.selectedValues.push(value);
} else if (!checked) {
this.selectedValues = this.selectedValues.filter(v => v !== value);
}
// Update trigger text without full re-render to keep dropdown open
const selectedText = this.selectedValues.length === 0
? this.container.dataset.placeholder
: this.selectedValues.length === 1
? this.selectedValues[0]
: `${this.selectedValues.length} selected`;
const contentSpan = this.container.querySelector('.multi-select-content span');
if (contentSpan) {
contentSpan.textContent = selectedText;
contentSpan.style.color = this.selectedValues.length === 0 ? 'var(--text-muted)' : 'var(--text-primary)';
}
filterReviewers();
}
removeOption(value) {
this.selectedValues = this.selectedValues.filter(v => v !== value);
this.render();
this.attachEvents();
filterReviewers();
}
clear() {
this.selectedValues = [];
this.render();
this.attachEvents();
}
getSelected() {
return this.selectedValues;
}
}
let categoryFilter, repoFilter, languageFilter;
function updateUrlParams() {
const searchInput = document.getElementById('search');
const searchTerm = searchInput ? searchInput.value : '';
const selectedCategories = categoryFilter ? categoryFilter.getSelected() : [];
const selectedRepos = repoFilter ? repoFilter.getSelected() : [];
const selectedLanguages = languageFilter ? languageFilter.getSelected() : [];
const params = new URLSearchParams();
if (searchTerm) {
params.set('search', searchTerm);
}
if (selectedCategories.length > 0) {
params.set('categories', selectedCategories.join(','));
}
if (selectedRepos.length > 0) {
params.set('repos', selectedRepos.join(','));
}
if (selectedLanguages.length > 0) {
params.set('languages', selectedLanguages.join(','));
}
const newUrl = params.toString() ? `${window.location.pathname}?${params.toString()}` : window.location.pathname;
history.replaceState(null, '', newUrl);
}
function loadFiltersFromUrl() {
const params = new URLSearchParams(window.location.search);
const searchTerm = params.get('search');
if (searchTerm) {
const searchInput = document.getElementById('search');
if (searchInput) searchInput.value = searchTerm;
}
const categories = params.get('categories');
if (categories && categoryFilter) {
categoryFilter.selectedValues = categories.split(',').filter(v => v.trim());
categoryFilter.render();
categoryFilter.attachEvents();
}
const repos = params.get('repos');
if (repos && repoFilter) {
repoFilter.selectedValues = repos.split(',').filter(v => v.trim());
repoFilter.render();
repoFilter.attachEvents();
}
const languages = params.get('languages');
if (languages && languageFilter) {
languageFilter.selectedValues = languages.split(',').filter(v => v.trim());
languageFilter.render();
languageFilter.attachEvents();
}
}
function filterReviewers() {
const searchInput = document.getElementById('search');
const searchTerm = searchInput ? searchInput.value.toLowerCase() : '';
const selectedCategories = categoryFilter ? categoryFilter.getSelected() : [];
const selectedRepos = repoFilter ? repoFilter.getSelected() : [];
const selectedLanguages = languageFilter ? languageFilter.getSelected() : [];
const cards = document.querySelectorAll('.reviewer-card');
let visibleCount = 0;
const initialView =
searchTerm === '' &&
selectedCategories.length === 0 &&
selectedRepos.length === 0 &&
selectedLanguages.length === 0;
cards.forEach(card => {
const titleEl = card.querySelector('.reviewer-title');
const descriptionEl = card.querySelector('.reviewer-description');
const title = titleEl ? titleEl.textContent.toLowerCase() : '';
const description = descriptionEl ? descriptionEl.textContent.toLowerCase() : '';
const repo = card.dataset.repo;
const category = card.dataset.category;
const language = card.dataset.language;
// Enhanced search: search across title, description, repo, category, and language
const matchesSearch = searchTerm === '' ||
title.includes(searchTerm) ||
description.includes(searchTerm) ||
repo.toLowerCase().includes(searchTerm) ||
category.toLowerCase().includes(searchTerm) ||
language.toLowerCase().includes(searchTerm);
const matchesCategory = selectedCategories.length === 0 || selectedCategories.includes(category);
const matchesRepo = selectedRepos.length === 0 || selectedRepos.includes(repo);
const matchesLanguage = selectedLanguages.length === 0 || selectedLanguages.includes(language);
if (matchesSearch && matchesCategory && matchesRepo && matchesLanguage) {
if (
initialView &&
card.classList.contains('extra-reviewer') &&
document.getElementById('load-more')
) {
card.style.display = 'none';
} else {
card.style.display = 'block';
visibleCount++;
}
} else {
card.style.display = 'none';
}
});
// Update the reviewer count
updateReviewerCount(
visibleCount,
initialView && document.getElementById('load-more')
? cards.length
: null
);
// Update URL parameters
updateUrlParams();
}
function updateReviewerCount(visible, total) {
const counterElement = document.getElementById('reviewer-count');
if (counterElement) {
counterElement.dataset.count = visible;
const totalVal =
typeof total === 'number' && total > visible ? total : null;
const parts = [visible.toLocaleString()];
if (totalVal) parts.push('of', totalVal.toLocaleString());
counterElement.textContent = parts.join(' ');
}
}
function clearFilters() {
const searchInput = document.getElementById('search');
if (searchInput) searchInput.value = '';
if (categoryFilter) categoryFilter.clear();
if (repoFilter) repoFilter.clear();
if (languageFilter) languageFilter.clear();
// Clear URL parameters
history.replaceState(null, '', window.location.pathname);
filterReviewers();
}
/**
* // Format stars with compact notation (800, 3.2K, 32K, etc.)
* @param num - the number to format
* @returns {string} - the formatted number, i.e. 3.2K
*/
function compactFormatting(num) {
if (num >= 1000000) {
return (num / 1000000).toFixed(1).replace(/\.0$/, '') + 'M';
} else if (num >= 1000) {
return (num / 1000).toFixed(1).replace(/\.0$/, '') + 'K';
} else {
return num.toString();
}
}
function formatStats() {
// Format star counts with compact notation
document.querySelectorAll('.stat-stars .stat-value[data-count]').forEach(el => {
const num = parseFloat(el.dataset.count);
if (!isNaN(num)) {
el.textContent = compactFormatting(num);
}
});
// Format comment counts with locale formatting
document.querySelectorAll('.stat-comments .stat-value[data-count]').forEach(el => {
const num = parseFloat(el.dataset.count);
if (!isNaN(num)) {
el.textContent = num.toLocaleString();
}
});
}
// Add event listeners when DOM is loaded
document.addEventListener('DOMContentLoaded', function () {
if (document.getElementById('search')) {
const searchInput = document.getElementById('search');
if (searchInput) searchInput.addEventListener('input', filterReviewers);
// Initialize multi-select filters if containers exist
const categoryContainer = document.getElementById('category-filter');
const repoContainer = document.getElementById('repo-filter');
const languageContainer = document.getElementById('language-filter');
if (categoryContainer) {
const categoryOptions = Array.from(document.querySelectorAll('[data-category]'))
.map(el => el.dataset.category)
.filter((v, i, a) => a.indexOf(v) === i)
.sort();
categoryFilter = new MultiSelect('category-filter', categoryOptions);
}
if (repoContainer) {
const repoOptions = Array.from(document.querySelectorAll('[data-repo]'))
.map(el => el.dataset.repo)
.filter((v, i, a) => a.indexOf(v) === i)
.sort();
repoFilter = new MultiSelect('repo-filter', repoOptions);
}
if (languageContainer) {
const languageOptions = Array.from(document.querySelectorAll('[data-language]'))
.map(el => el.dataset.language)
.filter((v, i, a) => a.indexOf(v) === i)
.sort();
languageFilter = new MultiSelect('language-filter', languageOptions);
}
formatStats();
// Load filters from URL parameters
loadFiltersFromUrl();
// Format initial reviewer count and apply filters
const allCards = document.querySelectorAll('.reviewer-card');
const totalCount = allCards.length;
const visibleCount = Array.from(allCards).filter(
c => !c.classList.contains('extra-reviewer')
).length;
updateReviewerCount(
visibleCount,
document.getElementById('load-more') ? totalCount : null
);
filterReviewers();
document.querySelectorAll('.reviewer-card').forEach(card => {
if (card.dataset.slug) {
card.addEventListener('click', () => {
const slug = card.dataset.slug;
openDrawer(slug);
history.replaceState(null, '', `#${slug}`);
});
}
});
const slug = location.hash.slice(1);
if (slug && document.getElementById(slug)) {
openDrawer(slug);
}
}
if (document.getElementById('discussion-container')) {
const parts = location.pathname.split('/').filter(Boolean);
const pageSlug = parts[parts.length - 1];
if (pageSlug) {
loadDiscussions(pageSlug);
}
}
});
function openDrawer(slug) {
const url = `/reviewers/${slug}/`;
const drawer = document.getElementById('drawer');
const content = document.getElementById('drawer-content');
fetch(url)
.then(res => {
if (!res.ok) {
throw new Error(`Server returned ${res.status} ${res.statusText}`);
}
return res.text();
})
.then(html => {
const doc = new DOMParser().parseFromString(html, 'text/html');
const detail = doc.querySelector('.reviewer-detail');
if (detail) {
const back = detail.querySelector('.back-link');
if (back) back.remove();
content.innerHTML = detail.innerHTML;
} else {
content.innerHTML = html;
}
drawer.classList.add('open');
// Highlight any code blocks in the newly loaded content
if (typeof highlightNewContent === 'function') {
highlightNewContent(content);
}
loadDiscussions(slug);
})
.catch(err => {
console.error('Fetch failed:', err);
});
}
function closeDrawer() {
const drawer = document.getElementById('drawer');
const content = document.getElementById('drawer-content');
drawer.classList.remove('open');
history.replaceState(null, '', window.location.pathname);
// Clear content after animation completes
setTimeout(() => {
content.innerHTML = '';
}, 300);
}
function deployToBaz() {
console.log('🚀 Deploy to baz clicked');
// Get the current reviewer slug from the URL hash
const slug = location.hash.slice(1);
if (!slug) {
console.log('No reviewer currently open');
return;
}
// Find the reviewer card element to get metadata
const reviewerCard = document.getElementById(slug);
if (!reviewerCard) {
console.log('Reviewer card not found for slug:', slug);
return;
}
// Get the prompt content from the drawer content if available
const drawerContent = document.getElementById('drawer-content');
let promptText = '';
if (drawerContent) {
const promptContent = drawerContent.querySelector('#prompt-content');
if (promptContent) {
promptText = promptContent.textContent.trim();
}
}
// Extract metadata from the card
const metadata = {
slug: slug,
repository: reviewerCard.dataset.repo,
category: reviewerCard.dataset.category,
language: reviewerCard.dataset.language,
title: reviewerCard.querySelector('.reviewer-title')?.textContent,
description: promptText,
};
console.log('Current Reviewer Metadata:', metadata);
// Pretty print the metadata
console.table(metadata);
// Compress and base64 encode the metadata
compressAndEncode(metadata);
}
async function compressAndEncode(data) {
try {
// Convert metadata to JSON string
const jsonString = JSON.stringify(data, null, 2);
console.log('JSON string length:', jsonString.length);
// Convert string to Uint8Array
const encoder = new TextEncoder();
const uint8Array = encoder.encode(jsonString);
// Compress using gzip
const compressionStream = new CompressionStream('gzip');
const writer = compressionStream.writable.getWriter();
const reader = compressionStream.readable.getReader();
// Write the data to compression stream
writer.write(uint8Array);
writer.close();
// Read the compressed data
const compressedChunks = [];
let done = false;
while (!done) {
const { value, done: streamDone } = await reader.read();
done = streamDone;
if (value) {
compressedChunks.push(value);
}
}
// Combine chunks into single Uint8Array
const compressedLength = compressedChunks.reduce((acc, chunk) => acc + chunk.length, 0);
const compressedData = new Uint8Array(compressedLength);
let offset = 0;
for (const chunk of compressedChunks) {
compressedData.set(chunk, offset);
offset += chunk.length;
}
// Convert to base64
let binary = '';
for (let i = 0; i < compressedData.length; i++) {
binary += String.fromCharCode(compressedData[i]);
}
const base64 = btoa(binary);
// URL encode the base64 string to ensure it's safe for URLs
const urlEncodedBase64 = encodeURIComponent(base64);
// Construct the full URL with fragment identifier
const deployUrl = `https://baz.co/agents/new-reviewer#reviewer_data=${urlEncodedBase64}`;
window.open(deployUrl, '_blank');
} catch (error) {
console.error('Error compressing data:', error);
}
}
function htmlToMarkdown(element) {
let markdown = '';
function processNode(node) {
if (node.nodeType === Node.TEXT_NODE) {
return node.textContent;
}
if (node.nodeType === Node.ELEMENT_NODE) {
const tagName = node.tagName.toLowerCase();
const children = Array.from(node.childNodes).map(processNode).join('');
switch (tagName) {
case 'h1': return `# ${children}\n\n`;
case 'h2': return `## ${children}\n\n`;
case 'h3': return `### ${children}\n\n`;
case 'h4': return `#### ${children}\n\n`;
case 'h5': return `##### ${children}\n\n`;
case 'h6': return `###### ${children}\n\n`;
case 'p': return `${children}\n\n`;
case 'strong': case 'b': return `**${children}**`;
case 'em': case 'i': return `*${children}*`;
case 'code':
// Check if this is inline code or part of a pre block
if (node.parentElement && node.parentElement.tagName.toLowerCase() === 'pre') {
return children;
}
return `\`${children}\``;
case 'pre':
const codeElement = node.querySelector('code');
if (codeElement) {
// Extract language from class (e.g., 'language-python' -> 'python')
const languageMatch = codeElement.className.match(/(?:language-|hljs-)(\w+)/);
const language = languageMatch ? languageMatch[1] : '';
return `\`\`\`${language}\n${codeElement.textContent}\n\`\`\`\n\n`;
}
return `\`\`\`\n${children}\n\`\`\`\n\n`;
case 'ul':
return `${children}\n`;
case 'ol':
return `${children}\n`;
case 'li':
const parent = node.parentElement;
if (parent.tagName.toLowerCase() === 'ol') {
// For ordered lists, we'll use '1.' for simplicity
return `1. ${children}\n`;
} else {
return `- ${children}\n`;
}
case 'blockquote':
return children.split('\n').map(line => `> ${line}`).join('\n') + '\n\n';
case 'br':
return '\n';
default:
return children;
}
}
return '';
}
markdown = processNode(element);
// Clean up extra newlines
markdown = markdown.replace(/\n{3,}/g, '\n\n').trim();
return markdown;
}
function copyPrompt() {
const promptContent = document.getElementById('prompt-content');
const copyButton = document.querySelector('.copy-button');
const copyText = document.getElementById('copy-text');
// Convert HTML content back to markdown format
const markdownContent = htmlToMarkdown(promptContent);
const textarea = document.createElement('textarea');
textarea.value = markdownContent;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
copyButton.classList.add('copied');
copyText.textContent = 'Copied!';
setTimeout(() => {
copyButton.classList.remove('copied');
copyText.textContent = 'Copy Prompt';
}, 2000);
}
async function copyDeeplink(slug, button) {
const url = `${window.location.origin}/reviewers/${slug}/`;
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(url);
} else {
const textarea = document.createElement('textarea');
textarea.value = url;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
}
button.classList.add('copied');
button.textContent = 'Copied!';
} catch (err) {
console.error('Copy failed', err);
} finally {
setTimeout(() => {
button.classList.remove('copied');
button.textContent = 'Share';
}, 2000);
}
}
function loadDiscussions(slug) {
const container = document.getElementById('discussion-container');
const statsEl = document.getElementById('discussion-stats');
const footerEl = document.getElementById('discussion-footer');
if (!container) return;
fetch(`/_reviewers/${slug}.json`)
.then(res => res.json())
.then(data => {
container.innerHTML = data.map((d, i) => renderDiscussion(d, i)).join('');
if (typeof highlightNewContent === 'function') {
highlightNewContent(container);
}
const commentCount = data.reduce((t, d) => t + (d.discussion_comments || []).length, 0);
const participants = new Set();
data.forEach(d => (d.discussion_comments || []).forEach(c => participants.add(c.comment_author)));
if (statsEl) statsEl.textContent = `${commentCount} Comments`;
if (footerEl) footerEl.textContent = `${participants.size} participants · ${commentCount} comments`;
})
.catch(err => console.error('Failed to load discussions', err));
}
function renderDiscussion(d, index) {
const code = d.commented_code ? renderCodeDiff(d.commented_code, index) : '';
const comments = (d.discussion_comments || []).map(renderComment).join('');
return `<div class="discussion">${code}<div class="chat-thread">${comments}</div></div>`;
}
function renderCodeDiff(diff, index) {
const lines = diff.split('\n');
const rendered = lines.map(line => {
const type = line.startsWith('+') ? 'added' : line.startsWith('-') ? 'removed' : 'context';
const symbol = line.startsWith('+') ? '+' : line.startsWith('-') ? '-' : '&nbsp;';
const content = escapeHtml(line.replace(/^[-+]/, '')) || '&nbsp;';
return `<div class="diff-line ${type}"><span class="diff-symbol">${symbol}</span><span class="diff-content">${content}</span></div>`;
}).join('');
const header = `<div class="diff-header">Changes #${index + 1}</div>`;
return `<div class="diff-block">${header}${rendered}</div>`;
}
function renderComment(c) {
const avatar = `https://github.com/${c.comment_author}.png?size=40`;
const body = escapeHtml(c.comment_body).replace(/\n/g, '<br>');
const time = new Date(c.comment_created_at).toLocaleDateString();
return `<div class="chat-message"><img class="chat-avatar" src="${avatar}" alt="${c.comment_author}"><div class="chat-bubble"><div class="chat-meta"><span class="chat-author">${c.comment_author}</span> <span class="chat-time">${time}</span></div><div class="chat-body">${body}</div></div></div>`;
}
function escapeHtml(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
function shareFromCard(e, slug) {
e.stopPropagation();
copyDeeplink(slug, e.currentTarget);
}
function shareFromDrawer(e) {
let slug = location.hash.slice(1);
if (!slug) {
const parts = window.location.pathname.split('/').filter(Boolean);
slug = parts[parts.length - 1];
}
if (slug) {
copyDeeplink(slug, e.currentTarget);
}
}
function loadMore() {
document.querySelectorAll('.extra-reviewer').forEach(card => {
card.style.display = 'block';
});
const btn = document.getElementById('load-more');
if (btn) btn.remove();
filterReviewers();
}
</script>
{% include modal.html %}
<script src="{{ '/assets/prism/prism.js' | relative_url }}"></script>
<script src="{{ '/assets/js/prism-init.js' | relative_url }}"></script>
<script src="{{ '/assets/js/main.js' | relative_url }}"></script>
</body>
</html>