Download source/src/components.jsx from mindchain/hub-flow: direct link, hf CLI and curl.
- Browser
- Download file 9.41 kB
-
https://huggingface.co/spaces/mindchain/hub-flow/resolve/main/source/src/components.jsx
- Command line
-
hf download hf://spaces/mindchain/hub-flow/source/src/components.jsx
-
curl -L -o components.jsx https://huggingface.co/spaces/mindchain/hub-flow/resolve/main/source/src/components.jsx
9.41 kB
| import React, { useEffect, useMemo, useRef, useState } from 'react' | |
| export const nf = new Intl.NumberFormat('en-US') | |
| export const fmt = (n) => (n == null || isNaN(n) ? '—' : nf.format(Math.round(n))) | |
| export const pct = (g) => (g == null || isNaN(g) ? '—' : (g > 0 ? '+' : '') + (g * 100).toFixed(0) + '%') | |
| export function useCountUp(target, dur = 900) { | |
| const [v, setV] = useState(0) | |
| useEffect(() => { | |
| let raf | |
| const t0 = performance.now() | |
| const tick = (t) => { | |
| const p = Math.min(1, (t - t0) / dur) | |
| setV(Math.round(target * (1 - Math.pow(1 - p, 3)))) | |
| if (p < 1) raf = requestAnimationFrame(tick) | |
| } | |
| raf = requestAnimationFrame(tick) | |
| return () => cancelAnimationFrame(raf) | |
| }, [target, dur]) | |
| return v | |
| } | |
| export function Stat({ k, v, d, dCls = '', sub }) { | |
| const n = useCountUp(v) | |
| return ( | |
| <div className="min-w-[128px] flex-1 px-4 py-3.5 sm:px-5"> | |
| <div className="text-[10.5px] font-semibold uppercase tracking-[0.08em] text-[var(--faint)]">{k}</div> | |
| <div className="tnum mt-0.5 text-[22px] font-bold tracking-tight sm:text-2xl">{fmt(n)}</div> | |
| {d != null && <div className={'mt-0.5 font-mono text-[11.5px] ' + dCls}>{d}</div>} | |
| {sub && <div className="text-[11px] text-[var(--faint)]">{sub}</div>} | |
| </div> | |
| ) | |
| } | |
| export function Sparkline({ data, w = 90, h = 26, color = 'var(--accent)', area = true }) { | |
| if (!data || data.length < 2) return null | |
| const max = Math.max(1, ...data) | |
| const min = Math.min(...data) | |
| const span = Math.max(1e-9, max - min) | |
| const pts = data.map((v, i) => [ | |
| (i / (data.length - 1)) * (w - 4) + 2, | |
| h - 3 - ((v - min) / span) * (h - 8), | |
| ]) | |
| const line = pts.map((p, i) => (i ? 'L' : 'M') + p[0].toFixed(1) + ' ' + p[1].toFixed(1)).join(' ') | |
| return ( | |
| <svg width={w} height={h} viewBox={'0 0 ' + w + ' ' + h} className="overflow-visible" aria-hidden="true"> | |
| {area && <path d={line + ' L' + pts[pts.length - 1][0].toFixed(1) + ' ' + (h - 1) + ' L' + pts[0][0].toFixed(1) + ' ' + (h - 1) + ' Z'} fill={color} opacity="0.13" />} | |
| <path d={line} fill="none" stroke={color} strokeWidth="1.6" strokeLinejoin="round" strokeLinecap="round" /> | |
| </svg> | |
| ) | |
| } | |
| // columnar series: {day:[], dl_all:[], dl30:[], likes:[]} | |
| // bars = daily downloads (diff of dl_all), line = cumulative | |
| export function BigChart({ series, height = 300 }) { | |
| const wrapRef = useRef(null) | |
| const [hover, setHover] = useState(null) | |
| const { days, daily, cum } = useMemo(() => { | |
| const out = { days: [], daily: [], cum: [] } | |
| if (!series || !series.day || series.day.length < 2) return out | |
| for (let i = 1; i < series.day.length; i++) { | |
| out.days.push(series.day[i]) | |
| out.daily.push(Math.max(0, series.dl_all[i] - series.dl_all[i - 1])) | |
| out.cum.push(series.dl_all[i]) | |
| } | |
| return out | |
| }, [series]) | |
| if (days.length < 2) { | |
| return <p className="py-10 text-center text-sm text-[var(--dim)]">Not enough history for this model yet.</p> | |
| } | |
| const W = 1000, H = height, pad = { l: 56, r: 14, t: 16, b: 26 } | |
| const iw = W - pad.l - pad.r, ih = H - pad.t - pad.b | |
| const dmax = Math.max(1, ...daily) | |
| const cmax = Math.max(1, ...cum) | |
| const x = (i) => pad.l + (i / (daily.length - 1)) * iw | |
| const yd = (v) => pad.t + ih - (v / dmax) * ih | |
| const yc = (v) => pad.t + ih - (v / cmax) * ih | |
| const bw = Math.max(1, iw / daily.length - 1.2) | |
| const bars = daily.map((v, i) => ( | |
| <rect key={i} x={x(i) - bw / 2} y={yd(v)} width={bw} height={pad.t + ih - yd(v)} rx={bw > 3 ? 1.5 : 0} | |
| fill={hover === i ? 'var(--accent)' : 'var(--bar)'} opacity={hover == null || hover === i ? 1 : 0.85} /> | |
| )) | |
| const cpts = cum.map((v, i) => x(i).toFixed(1) + ',' + yc(v).toFixed(1)).join(' ') | |
| const grid = [0, 1, 2, 3, 4].map((g) => { | |
| const gy = pad.t + ih - (g / 4) * ih | |
| return ( | |
| <g key={g}> | |
| <line x1={pad.l} y1={gy} x2={W - pad.r} y2={gy} stroke="var(--line2)" /> | |
| <text x={pad.l - 8} y={gy + 4} fontSize="10.5" fill="var(--faint)" textAnchor="end" className="tnum">{fmt((dmax * g) / 4)}</text> | |
| </g> | |
| ) | |
| }) | |
| const onMove = (e) => { | |
| const rect = wrapRef.current.getBoundingClientRect() | |
| const rel = (e.clientX - rect.left) / rect.width | |
| setHover(Math.max(0, Math.min(daily.length - 1, Math.round(rel * (daily.length - 1))))) | |
| } | |
| return ( | |
| <div ref={wrapRef} className="relative mt-2" onMouseMove={onMove} onMouseLeave={() => setHover(null)}> | |
| <svg viewBox={'0 0 ' + W + ' ' + H} className="w-full" role="img" aria-label="download history"> | |
| <defs> | |
| <linearGradient id="cumfade2" x1="0" y1="0" x2="0" y2="1"> | |
| <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.22" /> | |
| <stop offset="100%" stopColor="var(--accent)" stopOpacity="0" /> | |
| </linearGradient> | |
| </defs> | |
| {grid} | |
| {bars} | |
| <polygon points={x(0) + ',' + (pad.t + ih) + ' ' + cpts + ' ' + x(daily.length - 1) + ',' + (pad.t + ih)} fill="url(#cumfade2)" /> | |
| <polyline points={cpts} fill="none" stroke="var(--accent)" strokeWidth="2.2" strokeLinejoin="round" /> | |
| {hover != null && ( | |
| <g> | |
| <line x1={x(hover)} y1={pad.t} x2={x(hover)} y2={pad.t + ih} stroke="var(--faint)" strokeDasharray="3 3" /> | |
| <circle cx={x(hover)} cy={yc(cum[hover])} r="3.6" fill="var(--accent)" stroke="var(--bg)" strokeWidth="2" /> | |
| </g> | |
| )} | |
| {[days[0], days[Math.floor(days.length / 2)], days[days.length - 1]].map((t, i) => ( | |
| <text key={i} x={i === 0 ? pad.l : i === 1 ? W / 2 : W - pad.r} y={H - 8} fontSize="10.5" fill="var(--faint)" textAnchor={i === 0 ? 'start' : i === 1 ? 'middle' : 'end'}>{t}</text> | |
| ))} | |
| </svg> | |
| {hover != null && ( | |
| <div className="pointer-events-none absolute top-1 z-10 rounded-lg border border-[var(--line)] bg-[var(--panel)] px-3 py-2 text-xs shadow-xl" | |
| style={{ left: 'min(max(' + ((hover / Math.max(1, daily.length - 1)) * 100) + '% - 70px, 0px), calc(100% - 160px))' }}> | |
| <div className="font-semibold text-[var(--ink)]">{days[hover]}</div> | |
| <div className="tnum mt-0.5 text-[var(--dim)]">{fmt(daily[hover])} downloads/day</div> | |
| <div className="tnum text-[var(--accent)]">{fmt(cum[hover])} all time</div> | |
| </div> | |
| )} | |
| </div> | |
| ) | |
| } | |
| // simple area/line chart from (days[], vals[]) — used for the hub-wide pulse | |
| export function AreaChart({ days, vals, height = 240 }) { | |
| const wrapRef = useRef(null) | |
| const [hover, setHover] = useState(null) | |
| if (!days || days.length < 2) return null | |
| const W = 1000, H = height, pad = { l: 56, r: 14, t: 14, b: 26 } | |
| const iw = W - pad.l - pad.r, ih = H - pad.t - pad.b | |
| const max = Math.max(1, ...vals) | |
| const x = (i) => pad.l + (i / (days.length - 1)) * iw | |
| const y = (v) => pad.t + ih - (v / max) * ih | |
| const pts = vals.map((v, i) => x(i).toFixed(1) + ',' + y(v).toFixed(1)).join(' ') | |
| const grid = [0, 1, 2, 3, 4].map((g) => { | |
| const gy = pad.t + ih - (g / 4) * ih | |
| return ( | |
| <g key={g}> | |
| <line x1={pad.l} y1={gy} x2={W - pad.r} y2={gy} stroke="var(--line2)" /> | |
| <text x={pad.l - 8} y={gy + 4} fontSize="10.5" fill="var(--faint)" textAnchor="end" className="tnum">{fmt((max * g) / 4)}</text> | |
| </g> | |
| ) | |
| }) | |
| const onMove = (e) => { | |
| const rect = wrapRef.current.getBoundingClientRect() | |
| const rel = (e.clientX - rect.left) / rect.width | |
| setHover(Math.max(0, Math.min(days.length - 1, Math.round(rel * (days.length - 1))))) | |
| } | |
| return ( | |
| <div ref={wrapRef} className="relative" onMouseMove={onMove} onMouseLeave={() => setHover(null)}> | |
| <svg viewBox={'0 0 ' + W + ' ' + H} className="w-full" role="img" aria-label="hub downloads per day"> | |
| <defs> | |
| <linearGradient id="hubfade" x1="0" y1="0" x2="0" y2="1"> | |
| <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.3" /> | |
| <stop offset="100%" stopColor="var(--accent)" stopOpacity="0" /> | |
| </linearGradient> | |
| </defs> | |
| {grid} | |
| <polygon points={x(0) + ',' + (pad.t + ih) + ' ' + pts + ' ' + x(days.length - 1) + ',' + (pad.t + ih)} fill="url(#hubfade)" /> | |
| <polyline points={pts} fill="none" stroke="var(--accent)" strokeWidth="2.2" strokeLinejoin="round" /> | |
| {hover != null && ( | |
| <g> | |
| <line x1={x(hover)} y1={pad.t} x2={x(hover)} y2={pad.t + ih} stroke="var(--faint)" strokeDasharray="3 3" /> | |
| <circle cx={x(hover)} cy={y(vals[hover])} r="3.6" fill="var(--accent)" stroke="var(--bg)" strokeWidth="2" /> | |
| </g> | |
| )} | |
| {[days[0], days[Math.floor(days.length / 2)], days[days.length - 1]].map((t, i) => ( | |
| <text key={i} x={i === 0 ? pad.l : i === 1 ? W / 2 : W - pad.r} y={H - 8} fontSize="10.5" fill="var(--faint)" textAnchor={i === 0 ? 'start' : i === 1 ? 'middle' : 'end'}>{t}</text> | |
| ))} | |
| </svg> | |
| {hover != null && ( | |
| <div className="pointer-events-none absolute top-1 z-10 rounded-lg border border-[var(--line)] bg-[var(--panel)] px-3 py-2 text-xs shadow-xl" | |
| style={{ left: 'min(max(' + ((hover / Math.max(1, days.length - 1)) * 100) + '% - 70px, 0px), calc(100% - 170px))' }}> | |
| <div className="font-semibold text-[var(--ink)]">{days[hover]}</div> | |
| <div className="tnum mt-0.5 text-[var(--dim)]">{fmt(vals[hover])} downloads</div> | |
| </div> | |
| )} | |
| </div> | |
| ) | |
| } | |