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'''
{full_name}

{description}

{lang_section}
{format_number(stars)}
{format_number(forks)}
''' 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'{error}' 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''' {full_name} {desc} {lang_svg} {stars} {forks} ''' 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()