import React, { useEffect, useMemo, useState } from 'react'
import { api, hubTop } from './api.js'
import { BigChart, Stat, fmt, pct } from './components.jsx'
export function Loading({ label }) {
return (
{label || 'Loading…'}
)
}
export function ErrBox({ e, retry }) {
return (
Could not load data{e ? ' (' + e.message + ')' : ''}. The model-pulse API may be rate-limiting or down.
{retry &&
}
)
}
const REL_ORDER = ['quantized', 'finetune', 'adapter', 'merge']
const REL_LABEL = { quantized: 'Quantizations', finetune: 'Fine-tunes', adapter: 'Adapters', merge: 'Merges' }
export function ModelPage({ id, go }) {
const [data, setData] = useState(null)
const [err, setErr] = useState(null)
const load = () => { setErr(null); setData(null); api.model(id).then(setData).catch(setErr) }
useEffect(load, [id])
if (err) return
if (!data) return
const m = data.model || {}
const rel = data.series && data.series.day && data.series.day.length > 1
const groups = {}
;(data.children || []).forEach((c) => { (groups[c.relation] = groups[c.relation] || []).push(c) })
return (
{id}
{m.pipeline_tag && {m.pipeline_tag}}
{m.is_gguf && GGUF}
{m.license && {String(m.license).slice(0, 40)}}
{m.params > 0 &&
{(m.params / 1e9).toFixed(1)}B params}
{m.created_at &&
created {String(m.created_at).slice(0, 10)}}
{m.first_seen &&
tracked since {m.first_seen}}
{m.rank_dl30 != null &&
rank dl30 #{fmt(m.rank_dl30)}}
{m.rank_7d != null &&
rank 7d #{fmt(m.rank_7d)}}
open on the Hub ↗
= 0 ? 'text-[var(--up)]' : 'text-[var(--danger)]'} />
{m.fam_members != null && }
{m.trending != null && }
downloads / day
cumulative
{rel ? : No history yet — too new.
}
{(data.children || []).length > 0 && (
Family · {(data.children || []).length} derivatives (quantizations, fine-tunes, …)
{REL_ORDER.filter((r) => groups[r]).concat(Object.keys(groups).filter((r) => !REL_ORDER.includes(r))).map((rel2) => {
const list = groups[rel2].slice().sort((a, b) => b.dl30 - a.dl30).slice(0, 8)
const maxdl = Math.max(1, ...groups[rel2].map((c) => c.dl30))
return (
{REL_LABEL[rel2] || rel2} · {groups[rel2].length}
{list.map((c) => (
go('#/m/' + c.id)} className="cursor-pointer border-b border-[var(--line2)] transition-colors hover:bg-[var(--accent-soft)]/40">
| {c.id} |
|
{fmt(c.dl30)} |
{fmt(c.dl_all)} |
))}
{groups[rel2].length > 8 &&
+ {groups[rel2].length - 8} more
}
)
})}
)}
)
}
export function AuthorPage({ author, go }) {
const [data, setData] = useState(null)
const [live, setLive] = useState(null)
const [err, setErr] = useState(null)
const load = () => { setErr(null); setData(null); api.author(author).then(setData).catch(setErr); hubTop('downloads', 1000).then((a) => setLive(a.filter((m) => m.id.startsWith(author + '/')))).catch(() => {}) }
useEffect(load, [author])
if (err) return
if (!data) return
const models = (data.models || []).slice().sort((a, b) => (b.dl30 || 0) - (a.dl30 || 0))
const totals = data.totals || {}
return (
{author}
a + (m.dl30 || 0), 0)} />
a + (m.dl_all || 0), 0)} />
a + (m.likes || 0), 0)} />
Author downloads, all models combined
{data.series && data.series.day && data.series.day.length > 1 ? : No history yet.
}
Models · {models.length}
| Model |
30d |
All time |
♥ |
{models.map((m) => (
go('#/m/' + m.id)} className="cursor-pointer border-b border-[var(--line2)] transition-colors hover:bg-[var(--accent-soft)]/40">
| {m.id.replace(author + '/', '')}{m.pipeline_tag && {m.pipeline_tag}} |
{fmt(m.dl30)} |
{fmt(m.dl_all)} |
♥ {fmt(m.likes)} |
))}
{live && Cross-checked live with the Hub API ({live.length} public repos).
}
)
}