import gradio as gr
import requests
import io
import tempfile
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
# Language colors mapping (common languages)
LANG_COLORS = {
"Python": "#3572A5", "JavaScript": "#f1e05a", "TypeScript": "#3178c6",
"Java": "#b07219", "C++": "#f34b7d", "C": "#555555", "C#": "#178600",
"Go": "#00ADD8", "Rust": "#dea584", "Ruby": "#701516", "PHP": "#4F5D95",
"Swift": "#F05138", "Kotlin": "#A97BFF", "Scala": "#c22d40",
"HTML": "#e34c26", "CSS": "#563d7c", "Shell": "#89e051", "Lua": "#000080",
"R": "#198CE7", "Dart": "#00B4AB", "Vue": "#41b883", "Jupyter Notebook": "#DA5B0B",
}
def get_lang_color(lang):
return LANG_COLORS.get(lang, "#586069")
def fetch_repo_data(repo_id: str):
"""Fetch repository data from GitHub API"""
repo_id = repo_id.strip()
if repo_id.startswith("https://github.com/"):
repo_id = repo_id.replace("https://github.com/", "")
repo_id = repo_id.rstrip("/")
if "/" not in repo_id:
return None, "Invalid repo ID. Use format: owner/repo"
url = f"https://api.github.com/repos/{repo_id}"
try:
resp = requests.get(url, timeout=10)
if resp.status_code == 404:
return None, f"Repository '{repo_id}' not found"
if resp.status_code == 403:
return None, "GitHub API rate limit exceeded. Try again later."
resp.raise_for_status()
return resp.json(), None
except Exception as e:
return None, f"Error fetching data: {str(e)}"
def format_number(n):
if n >= 1000:
return f"{n/1000:.1f}k"
return str(n)
def generate_card_html(repo_id: str):
"""Generate a beautiful HTML card for the repository"""
data, error = fetch_repo_data(repo_id)
if error:
return f'
{error}
'
name = data.get("name", "")
full_name = data.get("full_name", "")
description = data.get("description") or "No description provided"
language = data.get("language") or ""
stars = data.get("stargazers_count", 0)
forks = data.get("forks_count", 0)
html_url = data.get("html_url", "#")
owner = data.get("owner", {})
avatar = owner.get("avatar_url", "")
lang_color = get_lang_color(language)
# Build language section separately
lang_section = ""
if language:
lang_section = f'''
{language}
'''
html = f'''
'''
return html
def generate_card_svg(repo_id: str):
"""Generate an SVG card for embedding"""
data, error = fetch_repo_data(repo_id)
if error:
return f''
name = data.get("name", "")
full_name = data.get("full_name", "")
desc = data.get("description") or "No description"
if len(desc) > 60:
desc = desc[:57] + "..."
language = data.get("language") or ""
stars = format_number(data.get("stargazers_count", 0))
forks = format_number(data.get("forks_count", 0))
lang_color = get_lang_color(language)
# Build language section separately
lang_svg = ""
if language:
lang_svg = f'{language}'
star_x = "160" if language else "28"
star_text_x = "178" if language else "46"
fork_x = "220" if language else "88"
fork_text_x = "248" if language else "116"
svg = f''''''
return svg
def generate_card_png(repo_id: str):
"""Generate a PNG image from the SVG card"""
svg_content = generate_card_svg(repo_id)
# Check if it's an error SVG
if "not found" in svg_content or "Error" in svg_content or "Invalid" in svg_content:
return None
try:
# Write SVG to a temporary file
with tempfile.NamedTemporaryFile(mode='w', suffix='.svg', delete=False) as f:
f.write(svg_content)
temp_svg_path = f.name
# Convert SVG to PNG
drawing = svg2rlg(temp_svg_path)
# Scale up for better quality
scale = 2
drawing.width *= scale
drawing.height *= scale
drawing.scale(scale, scale)
# Create PNG in memory
png_data = io.BytesIO()
renderPM.drawToFile(drawing, png_data, fmt="PNG", bg=0xFFFFFF)
png_data.seek(0)
# Save to a temporary file for Gradio
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as f:
f.write(png_data.read())
return f.name
except Exception as e:
print(f"Error generating PNG: {e}")
return None
# Create Gradio interface
with gr.Blocks(
title="GitHub Repo Card Generator",
theme=gr.themes.Soft(),
css="""
.container { max-width: 800px; margin: auto; }
.gr-button-primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; }
"""
) as demo:
gr.Markdown(
"""
# 🎴 GitHub Repository Card Generator
Generate beautiful, embeddable cards for any public GitHub repository.
Enter a repository ID (e.g., `facebook/react` or `huggingface/transformers`) to get started.
"""
)
with gr.Row():
repo_input = gr.Textbox(
label="Repository ID",
placeholder="owner/repo (e.g., microsoft/vscode)",
scale=4
)
generate_btn = gr.Button("Generate Card", variant="primary", scale=1)
gr.Examples(
examples=[
"huggingface/transformers",
"facebook/react",
"microsoft/vscode",
"openai/whisper",
"gradio-app/gradio"
],
inputs=repo_input
)
with gr.Tabs():
with gr.Tab("Preview"):
html_output = gr.HTML(label="Card Preview")
with gr.Tab("SVG Code"):
svg_output = gr.Code(label="SVG Code (for embedding)", language="html")
with gr.Tab("Download PNG"):
png_output = gr.File(label="Download PNG", file_types=[".png"])
# Event handlers
generate_btn.click(
fn=generate_card_html,
inputs=repo_input,
outputs=html_output
).then(
fn=generate_card_svg,
inputs=repo_input,
outputs=svg_output
).then(
fn=generate_card_png,
inputs=repo_input,
outputs=png_output
)
repo_input.submit(
fn=generate_card_html,
inputs=repo_input,
outputs=html_output
).then(
fn=generate_card_svg,
inputs=repo_input,
outputs=svg_output
).then(
fn=generate_card_png,
inputs=repo_input,
outputs=png_output
)
if __name__ == "__main__":
demo.launch()