Download demo/live.html from FluidInference/citenet-coreml: direct link, hf CLI and curl.
- Browser
- Download file 8.86 kB
-
https://huggingface.co/FluidInference/citenet-coreml/resolve/main/demo/live.html
- Command line
-
hf download hf://FluidInference/citenet-coreml/demo/live.html
-
curl -L -o live.html https://huggingface.co/FluidInference/citenet-coreml/resolve/main/demo/live.html
8.86 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>CiteNet Live</title> | |
| <style> | |
| :root { | |
| --bg: #f7f6f2; --panel: #ffffff; --ink: #1c1b19; --muted: #77746b; --line: #e6e3da; | |
| --ok: #1f7a4d; --ok-bg: #e2f2e8; --bad: #c2321f; --bad-bg: #fbe3de; | |
| --accent: #2f4fd6; --gpu: #1f7a4d; | |
| --mono: ui-monospace, "SF Mono", Menlo, monospace; | |
| --serif: "Iowan Old Style", Palatino, Georgia, serif; | |
| } | |
| @media (prefers-color-scheme: dark) { | |
| :root { | |
| --bg: #141412; --panel: #1c1b19; --ink: #ecebe6; --muted: #918e85; --line: #2d2c28; | |
| --ok: #4fc487; --ok-bg: #1c3326; --bad: #ff7a66; --bad-bg: #42201a; | |
| --accent: #8ea2ff; --gpu: #4fc487; | |
| } | |
| } | |
| * { box-sizing: border-box; } | |
| body { margin: 0; background: var(--bg); color: var(--ink); font: 15px/1.5 -apple-system, BlinkMacSystemFont, system-ui, sans-serif; } | |
| .wrap { max-width: 980px; margin: 0 auto; padding: 36px 20px 48px; } | |
| h1 { margin: 0; font-size: 30px; letter-spacing: -0.02em; } | |
| .lede { color: var(--muted); margin: 4px 0 20px; } | |
| .lede a { color: inherit; } | |
| .controls { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; } | |
| .controls button { font: 600 15px -apple-system, system-ui, sans-serif; border: 0; background: var(--ink); color: var(--bg); padding: 11px 18px; border-radius: 10px; cursor: pointer; } | |
| .controls label { color: var(--muted); font-size: 14px; display: flex; gap: 6px; align-items: center; cursor: pointer; } | |
| .nums { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; margin: 26px 0 10px; } | |
| .num .k { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: .07em; } | |
| .num .v { font-size: 38px; font-weight: 700; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; line-height: 1.15; } | |
| .num .r { color: var(--muted); font-size: 14px; font-variant-numeric: tabular-nums; } | |
| .num .r b { color: var(--ink); font-weight: 600; } | |
| .bar { display: flex; height: 6px; border-radius: 3px; overflow: hidden; background: var(--line); } | |
| .bar div { background: var(--ink); width: 0; transition: width .1s linear; } | |
| .meta { color: var(--muted); font-size: 13px; margin-top: 8px; font-variant-numeric: tabular-nums; min-height: 1.5em; } | |
| .cols { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 24px; margin-top: 26px; } | |
| h2 { font-size: 12px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); margin: 0 0 8px; font-weight: 600; } | |
| .tape { height: 420px; overflow: hidden; border: 1px solid var(--line); border-radius: 12px; background: var(--panel); padding: 8px 14px; | |
| -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent); mask-image: linear-gradient(to bottom, #000 70%, transparent); } | |
| .tape div { font: 14px/1.55 var(--serif); padding: 4px 0; border-bottom: 1px solid var(--line); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--muted); } | |
| .tape mark { background: var(--ok-bg); color: var(--ink); border-radius: 3px; padding: 0 2px; } | |
| .tape mark.bad { background: var(--bad-bg); box-shadow: inset 0 -2px 0 var(--bad); } | |
| .caught { list-style: none; margin: 0; padding: 0; max-height: 420px; overflow: hidden; } | |
| .caught li { padding: 8px 0; border-top: 1px solid var(--line); } | |
| .caught li:first-child { border-top: 0; } | |
| .caught .c { font: 13px var(--mono); color: var(--bad); overflow-wrap: anywhere; } | |
| .caught .w { color: var(--muted); font-size: 13.5px; } | |
| .note { color: var(--muted); font-size: 13px; margin-top: 10px; } | |
| .empty { color: var(--muted); font-size: 14px; } | |
| @media (max-width: 760px) { | |
| .wrap { padding: 24px 16px 40px; } | |
| .nums { grid-template-columns: minmax(0, 1fr); gap: 10px; } | |
| .num .v { font-size: 30px; } | |
| .cols { grid-template-columns: minmax(0, 1fr); } | |
| .tape { height: 300px; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="wrap"> | |
| <h1>CiteNet Live</h1> | |
| <div class="lede" id="lede">Real US court opinions, scanned for impossible citations as fast as the Mac can read them. <a href="/">Back</a></div> | |
| <div class="controls"> | |
| <button id="go">Scan</button> | |
| <label><input type="checkbox" id="loop"> keep going (loop the corpus)</label> | |
| </div> | |
| <div class="nums"> | |
| <div class="num"><div class="k">Text read</div><div class="v" id="n-bytes">0 GB</div><div class="r" id="r-bytes"> </div></div> | |
| <div class="num"><div class="k">Citations checked</div><div class="v" id="n-cites">0</div><div class="r" id="r-cites"> </div></div> | |
| <div class="num"><div class="k">Flagged</div><div class="v" id="n-flag">0</div><div class="r" id="r-flag"> </div></div> | |
| </div> | |
| <div class="bar"><div id="prog"></div></div> | |
| <div class="meta" id="meta"></div> | |
| <div class="cols"> | |
| <div> | |
| <h2>Citations going by</h2> | |
| <div class="tape" id="tape"><div class="empty" style="border:0">Press Scan.</div></div> | |
| </div> | |
| <div> | |
| <h2>Flagged</h2> | |
| <ul class="caught" id="caught"></ul> | |
| <div class="note">Unverified. Real opinions contain typos and OCR errors, and CiteNet's volume timelines are approximate, so some flags are its own mistakes.</div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| const $ = (id) => document.getElementById(id); | |
| const esc = (s) => s.replace(/&#(\d+);/g, (_, n) => String.fromCharCode(+n)).replace(/&/g, "&") | |
| .replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); | |
| const fmtB = (b) => b >= 1e9 ? (b / 1e9).toFixed(2) + " GB" : (b / 1e6).toFixed(0) + " MB"; | |
| const fmtR = (n) => n >= 1e6 ? (n / 1e6).toFixed(2) + "M" : n >= 1e3 ? Math.round(n / 1e3) + "k" : String(n); | |
| let info = null, es = null, seen = new Set(); | |
| function reset() { | |
| ["n-bytes", "n-cites", "n-flag"].forEach(id => $(id).textContent = "0"); | |
| $("n-bytes").textContent = "0 GB"; | |
| ["r-bytes", "r-cites", "r-flag"].forEach(id => $(id).innerHTML = " "); | |
| $("tape").innerHTML = ""; $("caught").innerHTML = ""; $("meta").textContent = ""; $("prog").style.width = "0"; | |
| seen = new Set(); | |
| } | |
| function stop() { if (es) { es.close(); es = null; } $("go").textContent = info ? `Scan ${fmtB(info.bytes)}` : "Scan"; } | |
| function onTick(ev) { | |
| if (ev.start) return; | |
| const loopRun = $("loop").checked; | |
| $("n-bytes").textContent = fmtB(ev.bytes); | |
| $("r-bytes").innerHTML = ev.done ? `in <b>${ev.t.toFixed(2)} s</b>` : `<b>${(ev.mbps / 1000).toFixed(2)} GB/s</b>`; | |
| $("n-cites").textContent = ev.checked.toLocaleString(); | |
| $("r-cites").innerHTML = ev.done ? `<b>${fmtR(Math.round(ev.checked / ev.t))}/s</b> average` : `<b>${fmtR(ev.cps)}/s</b>`; | |
| $("n-flag").textContent = ev.flagged.toLocaleString(); | |
| $("r-flag").innerHTML = ev.checked ? `${(100 * ev.flagged / ev.checked).toFixed(3)}% of citations` : " "; | |
| const total = info ? info.bytes : ev.bytes; | |
| $("prog").style.width = loopRun ? "100%" : Math.min(100, 100 * ev.bytes / total) + "%"; | |
| const lanes = ev.ane + ev.gpu; | |
| $("meta").textContent = (lanes ? `Neural Engine ${Math.round(100 * ev.ane / lanes)}% · GPU ${Math.round(100 * ev.gpu / lanes)}% · ` : "") + | |
| `4 CPU scan threads · ${ev.t.toFixed(1)} s` + (loopRun ? ` · pass ${ev.pass + 1}` : "") + | |
| (ev.done ? ` · done: ${info ? info.decisions.toLocaleString() + " decisions, " : ""}${ev.checked.toLocaleString()} citations` : ""); | |
| const tape = $("tape"); | |
| for (const s of ev.ticker) { | |
| const d = document.createElement("div"); | |
| const before = s.before.slice(-34), after = s.after.slice(0, 40); | |
| d.innerHTML = `…${esc(before)}<mark class="${s.flag ? "bad" : ""}">${esc(s.cite)}</mark>${esc(after)}…`; | |
| tape.prepend(d); | |
| } | |
| while (tape.children.length > 40) tape.lastChild.remove(); | |
| const ul = $("caught"); | |
| for (const c of ev.caught) { | |
| if (seen.has(c.cite)) continue; | |
| seen.add(c.cite); | |
| const li = document.createElement("li"); | |
| li.innerHTML = `<div class="c">${esc(c.cite)}</div><div class="w">${esc(c.why || c.kind)}</div>`; | |
| ul.prepend(li); | |
| } | |
| while (ul.children.length > 14) ul.lastChild.remove(); | |
| if (ev.done) stop(); | |
| } | |
| $("go").onclick = () => { | |
| if (es) return stop(); | |
| reset(); | |
| $("go").textContent = "Stop"; | |
| es = new EventSource("/live/stream?loop=" + ($("loop").checked ? 1 : 0)); | |
| es.onmessage = (m) => onTick(JSON.parse(m.data)); | |
| es.onerror = () => stop(); | |
| }; | |
| fetch("/live/info").then(r => r.json()).then(j => { | |
| if (j.error) { $("lede").innerHTML = "No corpus found (data/cold/corpus.txt). <a href='/'>Back</a>"; return; } | |
| info = j; | |
| $("lede").innerHTML = `${j.decisions.toLocaleString()} real US court decisions (${fmtB(j.bytes)}, COLD Cases, CC0), ` + | |
| `scanned by a native scanner on the CPU and checked by CiteNet on the Neural Engine + GPU. <a href="/wall">Watch it read</a> · <a href="/">Back</a>`; | |
| $("go").textContent = `Scan ${fmtB(j.bytes)}`; | |
| if (new URLSearchParams(location.search).get("auto")) $("go").click(); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |