Download index.html from pngwn/github-issue-classifier-demo: direct link, hf CLI and curl.
- Browser
- Download file 8.82 kB
-
https://huggingface.co/spaces/pngwn/github-issue-classifier-demo/resolve/main/index.html
- Command line
-
hf download hf://spaces/pngwn/github-issue-classifier-demo/index.html
-
curl -L -o index.html https://huggingface.co/spaces/pngwn/github-issue-classifier-demo/resolve/main/index.html
8.82 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>GitHub Issue Classifier</title> | |
| <style> | |
| :root { | |
| --ink: #0a0a0a; | |
| --muted: #666; | |
| --line: #eaeaea; | |
| --bg: #fafafa; | |
| --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; | |
| } | |
| * { box-sizing: border-box; } | |
| body { | |
| margin: 0; | |
| background: var(--bg); | |
| color: var(--ink); | |
| font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; | |
| -webkit-font-smoothing: antialiased; | |
| } | |
| .wrap { max-width: 680px; margin: 0 auto; padding: 56px 20px 64px; } | |
| .badge { | |
| display: inline-block; | |
| font-family: var(--mono); font-size: 12px; letter-spacing: .02em; | |
| color: var(--muted); | |
| border: 1px solid var(--line); border-radius: 999px; | |
| padding: 4px 12px; background: #fff; | |
| } | |
| h1 { font-size: 32px; font-weight: 650; letter-spacing: -0.035em; margin: 16px 0 10px; } | |
| .sub { font-size: 14px; color: var(--muted); line-height: 1.65; margin: 0; } | |
| .sub b { color: var(--ink); font-weight: 600; } | |
| .card { | |
| margin-top: 32px; | |
| background: #fff; | |
| border: 1px solid var(--line); | |
| border-radius: 12px; | |
| padding: 20px; | |
| box-shadow: 0 1px 2px rgba(0,0,0,.03); | |
| } | |
| textarea { | |
| width: 100%; min-height: 128px; resize: vertical; | |
| font-family: inherit; font-size: 14px; line-height: 1.55; | |
| color: var(--ink); | |
| border: 1px solid var(--line); border-radius: 10px; | |
| padding: 12px 14px; outline: none; | |
| transition: border-color .12s ease; | |
| background: #fff; | |
| } | |
| textarea:focus { border-color: #999; } | |
| textarea::placeholder { color: #b3b3b3; } | |
| .toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 14px; flex-wrap: wrap; } | |
| .pills { display: flex; gap: 8px; } | |
| .pill { | |
| font-size: 13px; color: var(--muted); | |
| border: 1px solid var(--line); border-radius: 999px; | |
| background: #fff; padding: 7px 14px; cursor: pointer; | |
| transition: all .12s ease; | |
| font-family: inherit; | |
| } | |
| .pill:hover { border-color: #999; color: var(--ink); } | |
| .pill.active { background: var(--ink); color: #fff; border-color: var(--ink); } | |
| .btn { | |
| font-size: 14px; font-weight: 500; | |
| background: #000; color: #fff; | |
| border: 1px solid #000; border-radius: 8px; | |
| padding: 10px 20px; cursor: pointer; | |
| box-shadow: 0 1px 2px rgba(0,0,0,.18); | |
| transition: background .12s ease; | |
| font-family: inherit; | |
| } | |
| .btn:hover { background: #333; } | |
| .btn:disabled { opacity: .55; cursor: default; } | |
| .examples { margin-top: 14px; display: flex; flex-direction: column; gap: 6px; } | |
| .examples-label { font-family: var(--mono); font-size: 11px; color: #999; letter-spacing: .06em; text-transform: uppercase; } | |
| .ex { | |
| text-align: left; font-size: 13px; color: var(--muted); | |
| background: none; border: none; padding: 5px 0; cursor: pointer; | |
| font-family: inherit; | |
| border-bottom: 1px solid transparent; | |
| white-space: nowrap; overflow: hidden; text-overflow: ellipsis; | |
| } | |
| .ex:hover { color: var(--ink); } | |
| .ex::before { content: "→ "; color: #b3b3b3; } | |
| .results { margin-top: 6px; } | |
| .results .hint { font-size: 13px; color: var(--muted); padding: 14px 2px; } | |
| .bar-row { | |
| display: grid; grid-template-columns: 76px 1fr 56px; | |
| gap: 12px; align-items: center; padding: 7px 0; | |
| } | |
| .bar-label { font-family: var(--mono); font-size: 12.5px; color: var(--muted); } | |
| .bar-row.top .bar-label { color: var(--ink); font-weight: 600; } | |
| .bar-track { | |
| height: 10px; border-radius: 5px; background: #f1f1f1; | |
| border: 1px solid var(--line); overflow: hidden; | |
| } | |
| .bar-fill { height: 100%; border-radius: 5px; background: #b8b8b8; transition: width .35s ease; } | |
| .bar-row.top .bar-fill { background: var(--ink); } | |
| .bar-pct { font-family: var(--mono); font-size: 12.5px; color: var(--muted); text-align: right; } | |
| .bar-row.top .bar-pct { color: var(--ink); font-weight: 600; } | |
| .verdict { | |
| margin-top: 12px; padding: 12px 14px; | |
| border: 1px solid var(--line); border-radius: 10px; background: #fff; | |
| font-size: 13.5px; color: var(--muted); | |
| display: none; | |
| } | |
| .verdict b { color: var(--ink); } | |
| .error { color: #c62828; } | |
| footer { | |
| margin-top: 36px; padding-top: 16px; | |
| border-top: 1px solid var(--line); | |
| font-size: 12.5px; color: var(--muted); line-height: 1.8; | |
| } | |
| footer a { color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--line); } | |
| footer a:hover { border-bottom-color: var(--ink); } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="wrap"> | |
| <span class="badge">pngwn / github-issues-4class</span> | |
| <h1>GitHub Issue Classifier</h1> | |
| <p class="sub">Paste an issue and it gets classified as <b>bug</b>, <b>feature</b>, <b>question</b> or <b>support</b>. | |
| Two models trained on 3,994 balanced issues — toggle between them below.</p> | |
| <div class="card"> | |
| <textarea id="input" placeholder="[Bug]: App crashes when opening the settings page after updating to v2.3.1…"></textarea> | |
| <div class="toolbar"> | |
| <div class="pills" id="pills"> | |
| <button class="pill active" data-model="setfit">SetFit · 22M</button> | |
| <button class="pill" data-model="distilbert">DistilBERT · 66M</button> | |
| </div> | |
| <button class="btn" id="go">Classify</button> | |
| </div> | |
| <div class="results" id="results"> | |
| <div class="hint">Results appear here.</div> | |
| </div> | |
| <div class="examples"> | |
| <span class="examples-label">Try one</span> | |
| <button class="ex">[Bug]: App crashes when opening the settings page after updating to v2.3.1. Stack trace attached — happens on every launch.</button> | |
| <button class="ex">Feature request: add keyboard shortcuts for switching between editor tabs, like VSCode has.</button> | |
| <button class="ex">How do I configure a custom API endpoint? The docs only cover the default one and I keep getting auth errors.</button> | |
| <button class="ex">I need help getting the CLI to work on Windows — after a clean install it says 'command not found'. Any ideas?</button> | |
| </div> | |
| </div> | |
| <footer> | |
| Models: <a href="https://huggingface.co/pngwn/github-issue-classifier-setfit-minilm">SetFit MiniLM (22M)</a> | |
| · <a href="https://huggingface.co/pngwn/github-issue-classifier-distilbert">DistilBERT (66M)</a> | |
| — Dataset: <a href="https://huggingface.co/datasets/pngwn/github-issues-4class">github-issues-4class</a>. | |
| SetFit: 0.807 macro-F1 · DistilBERT: 0.794 on the held-out test split. | |
| </footer> | |
| </div> | |
| <script type="module"> | |
| import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"; | |
| const ta = document.getElementById("input"); | |
| const go = document.getElementById("go"); | |
| const results = document.getElementById("results"); | |
| const pills = document.getElementById("pills"); | |
| let model = "setfit"; | |
| const client = await Client.connect(window.location.origin); | |
| pills.addEventListener("click", (e) => { | |
| const b = e.target.closest(".pill"); | |
| if (!b) return; | |
| model = b.dataset.model; | |
| pills.querySelectorAll(".pill").forEach((p) => p.classList.toggle("active", p === b)); | |
| }); | |
| document.querySelectorAll(".ex").forEach((b) => | |
| b.addEventListener("click", () => { ta.value = b.textContent; classify(); }) | |
| ); | |
| go.addEventListener("click", classify); | |
| ta.addEventListener("keydown", (e) => { if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) classify(); }); | |
| function render(data) { | |
| const top = data.probs.indexOf(Math.max(...data.probs)); | |
| const rows = data.labels | |
| .map((label, i) => { | |
| const pct = data.probs[i] * 100; | |
| return `<div class="bar-row${i === top ? " top" : ""}"> | |
| <div class="bar-label">${label}</div> | |
| <div class="bar-track"><div class="bar-fill" style="width:${pct.toFixed(1)}%"></div></div> | |
| <div class="bar-pct">${pct.toFixed(1)}%</div> | |
| </div>`; | |
| }) | |
| .join(""); | |
| results.innerHTML = | |
| `<div class="bars">${rows}</div> | |
| <div class="verdict" style="display:block">Predicted class: <b>${data.labels[top]}</b> (${(data.probs[top] * 100).toFixed(1)}% confidence)</div>`; | |
| } | |
| async function classify() { | |
| const text = ta.value.trim(); | |
| if (!text) return; | |
| go.disabled = true; | |
| go.textContent = "Classifying…"; | |
| results.innerHTML = '<div class="hint">Running…</div>'; | |
| try { | |
| const result = await client.predict("/classify", { text, model }); | |
| render(result.data[0]); | |
| } catch (err) { | |
| results.innerHTML = `<div class="hint error">Something went wrong: ${String(err.message || err)}</div>`; | |
| } finally { | |
| go.disabled = false; | |
| go.textContent = "Classify"; | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> |