""" GitHub Repository Intelligence Analyzer — Gradio Web UI Deployed on Hugging Face Spaces. """ import os import sys import json sys.path.insert(0, os.path.dirname(__file__)) import gradio as gr from src.analyzer import GitHubClient, analyze_repo from src.reporter import format_report, format_summary_table, to_json GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN") client = GitHubClient(token=GITHUB_TOKEN) EXAMPLE_REPOS = "\n".join([ "https://github.com/c2siorg/Webiu", "https://github.com/fastapi/fastapi", "https://github.com/pallets/flask", "https://github.com/huggingface/transformers", "https://github.com/torvalds/linux", ]) CUSTOM_CSS = """ @import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=JetBrains+Mono:wght@300;400;500&display=swap'); :root { --bg-0: #080b0f; --bg-1: #0d1117; --bg-2: #161b22; --bg-3: #1c2128; --border: #2a3441; --border-bright: #3a4a5c; --green: #3fb950; --green-dim: #1a4a2a; --amber: #d29922; --amber-dim: #3d2e0a; --red: #f85149; --blue: #388bfd; --text-1: #e6edf3; --text-2: #8b949e; --text-3: #484f58; --font-display: 'Syne', sans-serif; --font-mono: 'JetBrains Mono', monospace; } /* ── Reset everything ── */ *, *::before, *::after { box-sizing: border-box; } .gradio-container { background: var(--bg-0) !important; font-family: var(--font-mono) !important; max-width: 960px !important; margin: 0 auto !important; padding: 0 24px 64px !important; } /* ── Header ── */ .gh-header { padding: 56px 0 40px; border-bottom: 1px solid var(--border); margin-bottom: 40px; } .gh-header-eyebrow { font-family: var(--font-mono); font-size: 11px; font-weight: 500; letter-spacing: 0.15em; text-transform: uppercase; color: var(--green); margin-bottom: 16px; display: flex; align-items: center; gap: 8px; } .gh-header-eyebrow::before { content: ''; display: inline-block; width: 20px; height: 1px; background: var(--green); } .gh-header h1 { font-family: var(--font-display) !important; font-size: clamp(28px, 5vw, 42px) !important; font-weight: 800 !important; color: var(--text-1) !important; line-height: 1.1 !important; margin: 0 0 16px !important; letter-spacing: -0.02em !important; } .gh-header h1 span { color: var(--green); } .gh-header p { font-family: var(--font-mono) !important; font-size: 13px !important; color: var(--text-2) !important; margin: 0 !important; line-height: 1.7 !important; max-width: 560px !important; } /* ── Score cards strip ── */ .score-strip { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; margin-bottom: 32px; } .score-card { background: var(--bg-2); padding: 20px 24px; display: flex; flex-direction: column; gap: 6px; } .score-card-label { font-family: var(--font-mono); font-size: 10px; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-3); } .score-card-value { font-family: var(--font-display); font-size: 22px; font-weight: 700; color: var(--text-1); } .score-card-sub { font-family: var(--font-mono); font-size: 11px; color: var(--text-2); } /* ── Input area ── */ .input-section { margin-bottom: 28px; } .input-label { font-family: var(--font-mono); font-size: 11px; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-2); margin-bottom: 10px; display: block; } /* Override Gradio textarea */ .gradio-container textarea { background: var(--bg-2) !important; border: 1px solid var(--border) !important; border-radius: 8px !important; color: var(--text-1) !important; font-family: var(--font-mono) !important; font-size: 13px !important; line-height: 1.7 !important; padding: 16px !important; resize: vertical !important; transition: border-color 0.15s ease !important; } .gradio-container textarea:focus { border-color: var(--green) !important; outline: none !important; box-shadow: 0 0 0 3px rgba(63, 185, 80, 0.08) !important; } .gradio-container textarea::placeholder { color: var(--text-3) !important; } /* Labels */ .gradio-container label span, .gradio-container .label-wrap span { font-family: var(--font-mono) !important; font-size: 11px !important; font-weight: 500 !important; letter-spacing: 0.1em !important; text-transform: uppercase !important; color: var(--text-2) !important; } /* ── Button ── */ .gradio-container button.primary { background: var(--green) !important; color: #000 !important; font-family: var(--font-mono) !important; font-size: 13px !important; font-weight: 500 !important; letter-spacing: 0.06em !important; border: none !important; border-radius: 8px !important; padding: 12px 28px !important; cursor: pointer !important; transition: all 0.15s ease !important; width: 100% !important; margin-top: 12px !important; } .gradio-container button.primary:hover { background: #4fc660 !important; transform: translateY(-1px) !important; box-shadow: 0 4px 16px rgba(63, 185, 80, 0.25) !important; } .gradio-container button.primary:active { transform: translateY(0) !important; } /* ── Output tabs ── */ .gradio-container .tabs { border: 1px solid var(--border) !important; border-radius: 10px !important; overflow: hidden !important; background: var(--bg-1) !important; } .gradio-container .tab-nav { background: var(--bg-2) !important; border-bottom: 1px solid var(--border) !important; padding: 0 4px !important; gap: 0 !important; } .gradio-container .tab-nav button { font-family: var(--font-mono) !important; font-size: 12px !important; font-weight: 400 !important; letter-spacing: 0.05em !important; color: var(--text-2) !important; border: none !important; border-bottom: 2px solid transparent !important; border-radius: 0 !important; padding: 12px 20px !important; background: transparent !important; transition: color 0.15s !important; } .gradio-container .tab-nav button.selected { color: var(--green) !important; border-bottom-color: var(--green) !important; background: transparent !important; } .gradio-container .tabitem { background: var(--bg-1) !important; padding: 20px !important; } /* Output textbox */ .gradio-container .output-textbox textarea, .gradio-container .block textarea { background: transparent !important; border: none !important; color: var(--text-1) !important; font-family: var(--font-mono) !important; font-size: 12.5px !important; line-height: 1.75 !important; } /* Code block */ .gradio-container .codemirror-wrapper, .gradio-container code { background: transparent !important; font-family: var(--font-mono) !important; font-size: 12.5px !important; } /* ── Formula section ── */ .formula-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; margin-bottom: 32px; } .formula-card { background: var(--bg-2); padding: 20px 24px; } .formula-title { font-family: var(--font-display); font-size: 13px; font-weight: 700; color: var(--text-1); margin-bottom: 14px; display: flex; align-items: center; gap: 8px; } .formula-title .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--green); flex-shrink: 0; } .formula-title .dot.amber { background: var(--amber); } .formula-row { display: flex; justify-content: space-between; align-items: center; padding: 5px 0; border-bottom: 1px solid var(--bg-3); gap: 12px; } .formula-row:last-child { border-bottom: none; } .formula-metric { font-family: var(--font-mono); font-size: 11.5px; color: var(--text-2); } .formula-weight { font-family: var(--font-mono); font-size: 11px; font-weight: 500; color: var(--green); background: var(--green-dim); padding: 2px 8px; border-radius: 4px; white-space: nowrap; } .formula-weight.amber { color: var(--amber); background: var(--amber-dim); } /* ── Difficulty legend ── */ .legend-strip { display: flex; gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; margin-bottom: 40px; } .legend-item { flex: 1; background: var(--bg-2); padding: 14px 20px; display: flex; align-items: center; gap: 10px; } .legend-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; } .legend-text { font-family: var(--font-mono); font-size: 11.5px; color: var(--text-2); } .legend-text strong { display: block; color: var(--text-1); font-size: 12px; margin-bottom: 2px; } /* ── Footer ── */ .gh-footer { margin-top: 56px; padding-top: 24px; border-top: 1px solid var(--border); display: flex; justify-content: space-between; align-items: center; } .gh-footer-text { font-family: var(--font-mono); font-size: 11px; color: var(--text-3); } .gh-footer-badge { font-family: var(--font-mono); font-size: 10px; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; color: var(--green); background: var(--green-dim); padding: 4px 10px; border-radius: 4px; border: 1px solid rgba(63, 185, 80, 0.2); } /* ── Scrollbar ── */ ::-webkit-scrollbar { width: 6px; height: 6px; } ::-webkit-scrollbar-track { background: var(--bg-1); } ::-webkit-scrollbar-thumb { background: var(--border-bright); border-radius: 3px; } ::-webkit-scrollbar-thumb:hover { background: var(--text-3); } /* ── Misc Gradio overrides ── */ .gradio-container .block { background: transparent !important; border: none !important; padding: 0 !important; } .gradio-container .form { background: transparent !important; border: none !important; } .gradio-container footer { display: none !important; } .gradio-container .gap { gap: 16px !important; } /* Spinner */ .gradio-container .generating { border-color: var(--green) !important; } """ HEADER_HTML = """
Analyze GitHub repositories for activity, complexity, and contributor-facing learning difficulty — powered by the GitHub REST API.