mirror of
https://github.com/baz-scm/awesome-reviewers.git
synced 2025-08-20 18:58:52 +03:00
1007 lines
43 KiB
HTML
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>© <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('-') ? '-' : ' ';
|
|
const content = escapeHtml(line.replace(/^[-+]/, '')) || ' ';
|
|
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>
|