model-flow / source /src /App.jsx
mindchain's picture
Upload source/src/App.jsx with huggingface_hub
71a2649 verified
Raw History Blame Contribute Delete
24.4 kB
import React, { useEffect, useMemo, useRef, useState } from 'react'
const nf = new Intl.NumberFormat('en-US')
const fmt = (n) => (n == null ? '—' : nf.format(n))
const FAMILIES = [
{ key: 'imajev', label: 'Imajev', sub: 'vision decision models', match: /imajev/, chip: 'bg-[var(--accent)] text-black' },
{ key: 'jevk5', label: 'JevK5', sub: 'decision models', match: /jevk5/, chip: 'bg-emerald-400/90 text-black' },
{ key: 'decider', label: 'Decider', sub: 'typed decisions', match: /decider/, chip: 'bg-sky-400/90 text-black' },
{ key: 'train', label: 'Training & Experiments', sub: '', match: /qwen|grpo|t5|gemma|sdxl|kaggle|sft|dpo|colab|needle|arithmetic|sailing|functiongemma|reward|falcon|offload|outputs|selftest/i, fold: true },
{ key: 'legacy', label: 'Legacy', sub: '', match: /./, fold: true },
]
function dailyDeltas(series) {
const out = []
for (let i = 1; i < series.length; i++) out.push([series[i][0], Math.max(0, series[i][1] - series[i - 1][1])])
return out
}
function sumLast(deltas, n) {
const t = deltas.slice(-n)
return t.reduce((a, x) => a + x[1], 0)
}
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
}
function Sparkline({ data, w = 96, h = 28, color = 'var(--accent)' }) {
if (!data.length) return null
const max = Math.max(1, ...data.map((d) => d[1]))
const pts = data.map((d, i) => [
(i / Math.max(1, data.length - 1)) * (w - 4) + 2,
h - 3 - (d[1] / max) * (h - 8),
])
const line = pts.map((p, i) => (i ? 'L' : 'M') + p[0].toFixed(1) + ' ' + p[1].toFixed(1)).join(' ')
const area = line + ' L' + pts[pts.length - 1][0].toFixed(1) + ' ' + (h - 1) + ' L' + pts[0][0].toFixed(1) + ' ' + (h - 1) + ' Z'
return (
<svg width={w} height={h} viewBox={'0 0 ' + w + ' ' + h} className="overflow-visible" aria-hidden="true">
<path d={area} fill={color} opacity="0.13" />
<path d={line} fill="none" stroke={color} strokeWidth="1.7" strokeLinejoin="round" strokeLinecap="round" />
<circle cx={pts[pts.length - 1][0]} cy={pts[pts.length - 1][1]} r="2.2" fill={color} />
</svg>
)
}
function BigChart({ model }) {
const wrapRef = useRef(null)
const [hover, setHover] = useState(null)
const daily = useMemo(() => dailyDeltas(model.series), [model])
const cum = useMemo(() => daily.map((d, i) => [d[0], model.series[i + 1][1]]), [model])
const W = 1000, H = 300, pad = { l: 52, r: 16, t: 18, b: 28 }
if (daily.length < 2) {
return (
<p className="py-10 text-center text-sm text-[var(--dim)]">
Not enough history yet — first snapshot {model.series[0] ? model.series[0][0] : '?'} · come back tomorrow.
</p>
)
}
const dmax = Math.max(1, ...daily.map((d) => d[1]))
const cmax = Math.max(1, ...cum.map((d) => d[1]))
const iw = W - pad.l - pad.r, ih = H - pad.t - pad.b
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 cpts = cum.map((d, i) => x(i).toFixed(1) + ',' + yc(d[1]).toFixed(1)).join(' ')
const bars = daily.map((d, i) => {
const bw = Math.max(2, iw / daily.length - 2)
return (
<rect key={d[0]} x={x(i) - bw / 2} y={yd(d[1])} width={bw} height={pad.t + ih - yd(d[1])} rx="2"
fill={hover === i ? 'var(--accent)' : 'var(--bar)'} opacity={hover == null || hover === i ? 1 : 0.55}>
<title>{d[0] + ': ' + fmt(d[1]) + ' downloads'}</title>
</rect>
)
})
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(Math.round((dmax * g) / 4))}</text>
</g>
)
})
const onMove = (e) => {
const rect = wrapRef.current.getBoundingClientRect()
const rel = (e.clientX - rect.left) / rect.width
const i = Math.max(0, Math.min(daily.length - 1, Math.round(rel * (daily.length - 1))))
setHover(i)
}
const hd = hover != null ? daily[hover] : null
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 for ' + model.id}>
<defs>
<linearGradient id="cumfade" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="var(--accent)" stopOpacity="0.25" />
<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(#cumfade)" />
<polyline points={cpts} fill="none" stroke="var(--accent)" strokeWidth="2.6" 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][1])} r="4" fill="var(--accent)" stroke="var(--bg)" strokeWidth="2" />
</g>
)}
<text x={W - pad.r} y={yc(cum[cum.length - 1][1]) - 8} fontSize="11.5" fontWeight="700" fill="var(--accent)" textAnchor="end" className="tnum">
{fmt(cum[cum.length - 1][1])}
</text>
{[daily[0][0], daily[Math.floor(daily.length / 2)][0], daily[daily.length - 1][0]].map((t, i) => (
<text key={t} 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>
{hd && (
<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) + '% - 60px, 0px), calc(100% - 150px))' }}>
<div className="font-semibold text-[var(--ink)]">{hd[0]}</div>
<div className="tnum mt-0.5 text-[var(--dim)]">{fmt(hd[1])} downloads/day</div>
<div className="tnum text-[var(--accent)]">{fmt(cum[hover][1])} all time</div>
</div>
)}
</div>
)
}
function Stat({ k, v, d, dCls = '' }) {
const n = useCountUp(v)
return (
<div className="min-w-[130px] 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-2xl font-bold tracking-tight sm:text-[26px]">{fmt(n)}</div>
{d && <div className={'mt-0.5 font-mono text-[11.5px] ' + dCls}>{d}</div>}
</div>
)
}
function ModelDetail({ model, live, onClose }) {
const daily = useMemo(() => dailyDeltas(model.series), [model])
const best = daily.reduce((a, d) => (d[1] > (a ? a[1] : 0) ? d : a), null)
const all = live?.downloads ?? model.downloadsAllTime
return (
<div className="rise mt-2 rounded-xl border border-[var(--line)] bg-[var(--panel)] p-4 sm:p-6">
<div className="flex flex-wrap items-baseline justify-between gap-2">
<div>
<h3 className="font-mono text-base font-bold sm:text-lg">{model.id}</h3>
<div className="mt-1 flex flex-wrap gap-x-5 gap-y-1 text-xs text-[var(--dim)]">
<span className="tnum"><b className="text-[var(--ink)]">{fmt(all)}</b> all time</span>
<span className="tnum">best day <b className="text-[var(--ink)]">{best ? fmt(best[1]) : '—'}</b> {best ? best[0] : ''}</span>
<span className="tnum">♥ {fmt(live?.likes ?? model.likes)}</span>
{model.created && <span>since {model.created}</span>}
</div>
</div>
<div className="flex items-center gap-2">
<a href={'https://huggingface.co/' + model.id} target="_blank" rel="noopener"
className="rounded-lg border border-[var(--line)] px-3 py-1.5 text-xs font-semibold text-[var(--dim)] transition-colors hover:border-[var(--accent)] hover:text-[var(--ink)]">
Open on the Hub ↗
</a>
<button onClick={onClose} className="rounded-lg border border-[var(--line)] px-3 py-1.5 text-xs font-semibold text-[var(--dim)] transition-colors hover:text-[var(--ink)]">
Close
</button>
</div>
</div>
<div className="mt-1 flex gap-4 text-[11px] text-[var(--dim)]">
<span><i className="mr-1.5 inline-block h-2 w-2 rounded-[2px] bg-[var(--ink)]" />downloads / day</span>
<span><i className="mr-1.5 inline-block h-2 w-2 rounded-[2px] bg-[var(--accent)]" />cumulative</span>
</div>
<BigChart model={model} />
</div>
)
}
function ModelRow({ m, live, open, onToggle }) {
const deltas = useMemo(() => dailyDeltas(m.series), [m])
const d7 = sumLast(deltas, 7)
const all = live?.downloads ?? m.downloadsAllTime
const short = m.id.replace('mindchain/', '')
return (
<>
<tr onClick={onToggle} role="button" tabIndex={0} onKeyDown={(e) => { if (e.key === "Enter") onToggle() }} className="group cursor-pointer border-b border-[var(--line2)] transition-colors hover:bg-[var(--accent-soft)]/40">
<td className="py-3 pl-1 pr-3 align-middle">
<div className="break-all font-mono text-[13px] font-semibold">
<span className="text-[var(--faint)]">mindchain/</span>{short}
</div>
{m.pipeline && <div className="mt-0.5 truncate text-[11px] text-[var(--faint)]">{m.pipeline}</div>}
</td>
<td className="hidden px-3 py-3 align-middle md:table-cell"><Sparkline data={deltas.slice(-10)} /></td>
<td className="tnum px-3 py-3 text-right align-middle font-semibold">{fmt(all)}</td>
<td className={'tnum px-3 py-3 text-right align-middle font-mono text-[12.5px] ' + (d7 > 0 ? 'text-[var(--up)]' : 'text-[var(--faint)]')}>
{d7 > 0 ? '+' + fmt(d7) : '0'}
</td>
<td className="tnum hidden px-3 py-3 pr-1 text-right align-middle text-[var(--dim)] md:table-cell">♥ {fmt(live?.likes ?? m.likes)}</td>
</tr>
{open && (
<tr>
<td colSpan={5} className="border-b border-[var(--line2)] p-0">
<ModelDetail model={m} live={live} onClose={onToggle} />
</td>
</tr>
)}
</>
)
}
function SkeletonRow() {
return (
<tr className="border-b border-[var(--line2)]">
<td className="py-4 pl-1"><div className="h-4 w-2/3 animate-pulse rounded bg-[var(--line2)]" /></td>
<td className="hidden md:table-cell"><div className="h-5 w-24 animate-pulse rounded bg-[var(--line2)]" /></td>
<td><div className="ml-auto h-4 w-14 animate-pulse rounded bg-[var(--line2)]" /></td>
<td><div className="ml-auto h-4 w-12 animate-pulse rounded bg-[var(--line2)]" /></td>
<td className="hidden md:table-cell"><div className="ml-auto h-4 w-8 animate-pulse rounded bg-[var(--line2)]" /></td>
</tr>
)
}
export default function App() {
const [data, setData] = useState(null)
const [error, setError] = useState(false)
const [live, setLive] = useState({})
const [liveOk, setLiveOk] = useState(false)
const [openId, setOpenId] = useState(null)
const [query, setQuery] = useState('')
const [sortBy, setSortBy] = useState('downloads')
const [theme, setTheme] = useState(() => (localStorage.getItem('mf-theme') || 'dark'))
const searchRef = useRef(null)
useEffect(() => {
document.documentElement.classList.toggle('light', theme === 'light')
localStorage.setItem('mf-theme', theme)
}, [theme])
useEffect(() => {
fetch('data.json').then((r) => { if (!r.ok) throw new Error(r.status); return r.json() })
.then((d) => setData(d)).catch(() => setError(true))
}, [])
useEffect(() => {
fetch('https://huggingface.co/api/models?author=mindchain&limit=100&full=true')
.then((r) => { if (!r.ok) throw new Error(r.status); return r.json() })
.then((arr) => { const map = {}; arr.forEach((m) => (map[m.id] = m)); setLive(map); setLiveOk(true) })
.catch(() => setLiveOk(false))
}, [])
useEffect(() => {
const apply = () => {
const match = location.hash.startsWith('#/m/') ? location.hash.slice(4) : null
setOpenId(match ? decodeURIComponent(match) : null)
}
apply()
window.addEventListener('hashchange', apply)
return () => window.removeEventListener('hashchange', apply)
}, [])
useEffect(() => {
const onKey = (e) => {
if (e.key === '/' && document.activeElement !== searchRef.current) { e.preventDefault(); searchRef.current?.focus() }
if (e.key === 'Escape') { setOpenId(null); history.replaceState(null, '', location.pathname); searchRef.current?.blur() }
}
window.addEventListener('keydown', onKey)
return () => window.removeEventListener('keydown', onKey)
}, [])
const toggle = (id) => {
if (openId === id) { setOpenId(null); history.replaceState(null, '', location.pathname) }
else { setOpenId(id); location.hash = '#/m/' + encodeURIComponent(id) }
}
const models = data?.models ?? []
const get = (m, k) => (k === 'downloads' ? (live[m.id]?.downloads ?? m.downloadsAllTime) : k === 'delta7' ? sumLast(dailyDeltas(m.series), 7) : m.id)
const groups = useMemo(() => {
const q = query.trim().toLowerCase()
const out = []
for (const f of FAMILIES) {
let ms = models.filter((m) => f.match.test(m.id))
if (q) ms = ms.filter((m) => m.id.toLowerCase().includes(q))
ms = ms.slice().sort((a, b) => (sortBy === 'name' ? a.id.localeCompare(b.id) : get(b, sortBy) - get(a, sortBy)))
if (ms.length) out.push({ ...f, models: ms })
}
return out
}, [models, query, sortBy, live])
const port = models.filter((m) => /imajev|jevk5|decider/.test(m.id))
const tot = (arr) => arr.reduce((a, m) => a + (live[m.id]?.downloads ?? m.downloadsAllTime), 0)
const d7all = models.reduce((a, m) => a + sumLast(dailyDeltas(m.series), 7), 0)
const d7prev = models.reduce((a, m) => { const t = dailyDeltas(m.series).slice(-14, -7); return a + t.reduce((x, y) => x + y[1], 0) }, 0)
let best = { v: 0, day: null }
models.forEach((m) => dailyDeltas(m.series).forEach((d) => { if (d[1] > best.v) best = { v: d[1], day: d[0] } }))
const lastDay = models[0]?.series[models[0].series.length - 1]?.[0]
return (
<div className="min-h-screen">
<header className="sticky top-0 z-40 border-b border-[var(--line)] bg-[var(--bg)]/95">
<div className="mx-auto flex max-w-6xl flex-wrap items-center gap-x-4 gap-y-2 px-4 py-3 sm:px-6">
<a href="#" className="flex items-baseline gap-1 text-xl font-extrabold tracking-tight" onClick={(e) => { e.preventDefault(); setOpenId(null); setQuery(''); history.replaceState(null, '', location.pathname); window.scrollTo({ top: 0, behavior: 'smooth' }) }}>
model<span className="text-[var(--accent)]">·</span><span className="text-[var(--dim)]">flow</span>
</a>
<span className={'inline-flex items-center gap-1.5 rounded-full border border-[var(--line)] px-2.5 py-0.5 font-mono text-[11px] text-[var(--dim)]'}>
<span className={'h-[7px] w-[7px] rounded-full ' + (liveOk ? 'live-dot bg-[var(--up)]' : 'bg-[var(--faint)]')} />
{liveOk ? 'live from hub' : 'snapshot ' + (data?.generated ?? '…')}
</span>
<div className="ml-auto flex items-center gap-2">
<input ref={searchRef} value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Search models /"
className="w-40 rounded-lg border border-[var(--line)] bg-[var(--panel)] px-3 py-1.5 font-mono text-xs text-[var(--ink)] placeholder-[var(--faint)] outline-none transition-colors focus:border-[var(--accent)] sm:w-52" />
<select value={sortBy} onChange={(e) => setSortBy(e.target.value)}
className="rounded-lg border border-[var(--line)] bg-[var(--panel)] px-2 py-1.5 text-xs text-[var(--dim)] outline-none">
<option value="downloads">All time</option>
<option value="delta7">7 days</option>
<option value="name">A–Z</option>
</select>
<button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')} aria-label="toggle theme"
className="rounded-lg border border-[var(--line)] px-2.5 py-1.5 text-xs text-[var(--dim)] transition-colors hover:text-[var(--ink)]">
{theme === 'dark' ? '☾' : '☀'}
</button>
</div>
</div>
</header>
<div className="grid-tex pointer-events-none absolute inset-x-0 top-0 h-56 opacity-40" aria-hidden="true" />
<main className="relative mx-auto max-w-6xl px-4 pb-16 sm:px-6">
<div className="pt-10 sm:pt-14">
<h1 className="max-w-2xl text-3xl font-extrabold leading-[1.1] tracking-tight sm:text-[40px]">
Every download of the <span className="text-[var(--accent)]">mindchain</span> portfolio, day by day.
</h1>
<p className="mt-3 max-w-xl text-sm leading-relaxed text-[var(--dim)] sm:text-[15px]">
Imajev, JevK5 and Decider — decision models that run on an 8 GB card.
Numbers overlay live from the {' '}
<a className="underline decoration-[var(--line)] underline-offset-2 hover:decoration-[var(--dim)]" href="https://huggingface.co/api/models" target="_blank" rel="noopener">Hub API</a>,
history comes from daily snapshots.
</p>
</div>
{error && (
<div className="mt-10 rounded-xl border border-[var(--danger)]/40 bg-[var(--danger)]/5 p-8 text-center">
<p className="text-sm text-[var(--ink)]">Could not load the download history.</p>
<button onClick={() => location.reload()} className="mt-3 rounded-lg border border-[var(--line)] px-4 py-2 text-sm font-semibold hover:border-[var(--accent)]">Retry</button>
</div>
)}
{!data && !error && (
<div className="mt-10 rounded-xl border border-[var(--line)]">
<div className="flex flex-wrap gap-0 divide-x divide-[var(--line)]">
{[0, 1, 2, 3, 4].map((i) => (<div key={i} className="min-w-[130px] flex-1 px-5 py-4"><div className="h-3 w-20 animate-pulse rounded bg-[var(--line2)]" /><div className="mt-2 h-6 w-16 animate-pulse rounded bg-[var(--line2)]" /></div>))}
</div>
<table className="w-full"><tbody>{[0, 1, 2, 3, 4, 5].map((i) => (<SkeletonRow key={i} />))}</tbody></table>
</div>
)}
{data && (
<>
<div className="rise mt-8 grid grid-cols-2 divide-[var(--line)] overflow-hidden rounded-2xl border border-[var(--line)] bg-[var(--panel)] sm:grid-cols-3 lg:grid-cols-5 lg:divide-x">
<Stat k="Portfolio downloads" v={tot(port)} d={fmt(tot(models)) + ' incl. legacy'} />
<Stat k="Last 7 days" v={d7all} d={'vs ' + fmt(d7prev) + ' before'} dCls="text-[var(--up)]" />
<Stat k="Best day" v={best.v} d={best.day ?? ''} />
<Stat k="Models tracked" v={models.length} d={'snapshot ' + (lastDay ?? '')} />
<Stat k="Likes" v={models.reduce((a, m) => a + (live[m.id]?.likes ?? m.likes), 0)} d="on the hub" />
</div>
<div className="mt-6 flex flex-wrap gap-2">
{groups.map((g) => (
<a key={g.key} href={'#f-' + g.key}
className="rounded-full border border-[var(--line)] px-3.5 py-1.5 text-xs font-semibold text-[var(--dim)] transition-colors hover:border-[var(--accent)] hover:text-[var(--ink)]">
{g.label} <span className="tnum text-[var(--faint)]">{g.models.length}</span>
</a>
))}
</div>
{groups.length === 0 && (
<div className="mt-16 text-center">
<p className="text-sm text-[var(--dim)]">No models match <span className="font-mono text-[var(--ink)]">{query}</span></p>
<button onClick={() => setQuery('')} className="mt-3 rounded-lg border border-[var(--line)] px-4 py-2 text-xs font-semibold hover:border-[var(--accent)]">Clear search</button>
</div>
)}
{groups.map((g) => {
const gtot = tot(g.models)
return (
<section key={g.key} id={'f-' + g.key} className="mt-10 scroll-mt-20">
<div className="flex items-baseline justify-between gap-3 border-b border-[var(--ink)] pb-2">
<h2 className="flex items-center gap-2.5 text-[13px] font-bold uppercase tracking-[0.08em]">
<span className={'inline-block h-2 w-2 rounded-full ' + g.chip} />
{g.label}
{g.sub && <span className="font-medium normal-case tracking-normal text-[var(--faint)]">· {g.sub}</span>}
</h2>
<span className="tnum font-mono text-[11.5px] text-[var(--faint)]">{g.models.length} models · {fmt(gtot)} downloads</span>
</div>
<table className="mt-1 w-full">
<thead>
<tr className="text-[10.5px] uppercase tracking-[0.06em] text-[var(--faint)]">
<th className="py-2 pl-1 text-left font-semibold">Model</th>
<th className="hidden px-3 py-2 text-left font-semibold md:table-cell">Last 10 days</th>
<th className="px-3 py-2 text-right font-semibold">All time</th>
<th className="px-3 py-2 text-right font-semibold">Δ 7d</th>
<th className="hidden px-3 py-2 pr-1 text-right font-semibold md:table-cell">♥</th>
</tr>
</thead>
<tbody>
{g.models.map((m) => (
<ModelRow key={m.id} m={m} live={live[m.id]} open={openId === m.id} onToggle={() => toggle(m.id)} />
))}
</tbody>
</table>
</section>
)
})}
<footer className="mt-16 border-t border-[var(--line)] pt-6 text-xs leading-relaxed text-[var(--faint)]">
<p>
<b className="text-[var(--dim)]">Data:</b> daily snapshots from{' '}
<a className="underline decoration-[var(--line)] underline-offset-2 hover:decoration-[var(--dim)]" href="https://huggingface.co/datasets/modelpulse/model-pulse-data" target="_blank" rel="noopener">modelpulse/model-pulse-data</a>{' '}
(builds on <a className="underline decoration-[var(--line)] underline-offset-2 hover:decoration-[var(--dim)]" href="https://huggingface.co/datasets/cfahlgren1/hub-stats" target="_blank" rel="noopener">cfahlgren1/hub-stats</a>),
current values live from the public Hub API. Companion dataset:{' '}
<a className="underline decoration-[var(--line)] underline-offset-2 hover:decoration-[var(--dim)]" href="https://huggingface.co/datasets/mindchain/model-flow-data" target="_blank" rel="noopener">mindchain/model-flow-data</a>.
</p>
<p className="mt-1.5">
<b className="text-[var(--dim)]">model-flow</b> is an independent project, not affiliated with Hugging Face. Inspired by{' '}
<a className="underline decoration-[var(--line)] underline-offset-2 hover:decoration-[var(--dim)]" href="https://huggingface.co/spaces/tardellirs/model-pulse" target="_blank" rel="noopener">model-pulse</a>.
Built with React &amp; Tailwind. Press <kbd className="rounded border border-[var(--line)] px-1 font-mono">/</kbd> to search.
</p>
</footer>
</>
)}
</main>
</div>
)
}