chore: merge dev into v2 (#35591)

Co-authored-by: Frank <frank@anoma.ly>
Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com>
Co-authored-by: Brendan Allan <git@brendonovich.dev>
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Jack <jack@anoma.ly>
Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com>
Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com>
Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: James Long <longster@gmail.com>
Co-authored-by: Dustin Deus <deusdustin@gmail.com>
Co-authored-by: starptech <starptech@starptechs-MBP.fritz.box>
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
Co-authored-by: 𝓛𝓲𝓽𝓽𝓵𝓮 𝓕𝓻𝓪𝓷𝓴 <little-frank@opencord.local>
Co-authored-by: Dax <mail@thdxr.com>
Co-authored-by: usrnk1 <7547651+usrnk1@users.noreply.github.com>
Co-authored-by: Jay <53023+jayair@users.noreply.github.com>
Co-authored-by: runvip <164729189+runvip@users.noreply.github.com>
Co-authored-by: opencode <opencode@sst.dev>
Co-authored-by: Julian Coy <julian@ex-machina.co>
Co-authored-by: Vladimir Glafirov <vglafirov@gitlab.com>
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
Co-authored-by: Kit Langton <kit.langton@gmail.com>
Co-authored-by: Simon Klee <hello@simonklee.dk>
Co-authored-by: Jay <air@live.ca>
Co-authored-by: David Hill <1879069+iamdavidhill@users.noreply.github.com>
This commit is contained in:
Aiden Cline
2026-07-06 16:05:29 -05:00
committed by GitHub
co-authored by Frank Aarav Sareen Brendan Allan opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Jack Brendan Allan Shoubhit Dash opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> James Long Dustin Deus starptech Luke Parker 𝓛𝓲𝓽𝓽𝓵𝓮 𝓕𝓻𝓪𝓷𝓴 Dax usrnk1 Jay runvip opencode Julian Coy Vladimir Glafirov Adam Kit Langton Simon Klee Jay David Hill
parent f87998f37f
commit 9e0d3976e1
332 changed files with 24650 additions and 4497 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/stats-app",
"version": "1.17.13",
"version": "1.17.14",
"private": true,
"type": "module",
"license": "MIT",
+11 -2
View File
@@ -4,8 +4,10 @@ import { createStore } from "solid-js/store"
import { createSimpleContext } from "@opencode-ai/ui/context"
import {
LOCALES,
cookie,
detectFromLanguages,
dir,
fromPathname,
label,
localeFromCookieHeader,
localeFromRequest,
@@ -19,11 +21,16 @@ function initial() {
const event = getRequestEvent()
if (event) return localeFromRequest(event.request)
if (typeof window === "object") {
const fromPath = fromPathname(window.location.pathname)
if (fromPath) return fromPath
}
if (typeof document === "object") {
const fromDom = parseLocale(document.documentElement.dataset.locale)
if (fromDom) return fromDom
const fromCookie = localeFromCookieHeader(document.cookie)
if (fromCookie) return fromCookie
const fromDom = parseLocale(document.documentElement.dataset.locale)
if (fromDom) return fromDom
}
if (typeof navigator !== "object") return "en" satisfies Locale
@@ -55,6 +62,8 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
},
setLocale(next: Locale) {
setStore("locale", next)
if (typeof document !== "object") return
document.cookie = cookie(next)
},
}
},
+3 -3
View File
@@ -154,14 +154,14 @@ const en = {
"lab.notFound": "No models matched this lab.",
"lab.heroPrefix": "Explore {{count}} {{lab}} models used in OpenCode",
"lab.heroIncluding": "including {{models}}",
"lab.heroSuffix": "Compare recent token usage, context windows, release dates, and model-specific data.",
"lab.heroSuffix": "Compare recent token usage, context windows, release dates, and model data.",
"lab.tokensProcessed": "Tokens Processed",
"lab.pending": "Pending",
"lab.usageAfterActivity": "Usage appears after model activity lands",
"lab.latestRelease": "Latest release {{date}}",
"lab.shareOfUsage": "{{share}} of recent OpenCode usage",
"lab.usageTitle": "{{lab}} token usage",
"lab.usageDescription": "Daily OpenCode token volume over the last two months.",
"lab.usageDescription": "Daily token volume.",
"lab.noUsageTitle": "No usage yet",
"lab.noUsageDescription": "Recent token usage appears here once this lab has activity.",
"lab.dailyTokenChart": "{{lab}} daily token usage chart",
@@ -198,7 +198,7 @@ const en = {
"model.knowledge": "Knowledge",
"model.release": "Release",
"model.inputs": "Inputs",
"model.overviewDescription": "Recent OpenCode Go tokens, unique users, and market position.",
"model.overviewDescription": "Recent tokens, unique users, and market position.",
"model.noSummaryTitle": "No usage summary",
"model.noSummaryDescription": "This model has no OpenCode Go usage rows yet.",
"model.tokens": "Tokens",
+429 -312
View File
@@ -27,6 +27,7 @@ import {
findModelCatalogEntry,
formatCatalogLabName,
getModelCatalog,
type ModelCatalog,
type ModelCatalogCost,
type ModelCatalogEntry,
} from "../model-catalog"
@@ -47,6 +48,7 @@ import {
const statsUnfurlPath = "banner.png"
const geoMapWidth = 960
const geoMapHeight = 430
const shortMonths = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"] as const
type IsoCountryCode = readonly [string, string, string]
type WorldCountryProperties = GeoJsonProperties & { name?: string }
@@ -118,8 +120,7 @@ export default function StatsModel() {
const statsUnfurlUrl = new URL(statsUnfurlPath, localizedUrl("en", "/data/")).toString()
const modelHeaderLinks = createMemo<readonly HeaderLink[]>(() => [
{ href: "#overview", label: i18n.t("nav.overview") },
{ href: "#usage", label: i18n.t("nav.usage") },
{ href: "#users", label: i18n.t("nav.users") },
{ href: "#momentum", label: i18n.t("model.momentum") },
{ href: "#efficiency", label: i18n.t("nav.efficiency") },
{ href: "#geo-breakdown", label: i18n.t("nav.geoBreakdown") },
{ href: "#peers", label: i18n.t("nav.peers") },
@@ -174,10 +175,14 @@ export default function StatsModel() {
<Show when={catalogEntry() || stats() !== undefined} fallback={<ModelLoading />}>
<Show when={catalogEntry() || stats()} fallback={<ModelNotFound lab={labParam()} model={modelParam()} />}>
<>
<ModelHero data={stats() ?? null} catalog={catalogEntry() ?? null} labName={labName()} />
<ModelOverview data={stats() ?? null} />
<ModelUsageSection data={stats()?.usage ?? []} />
<ModelUsersSection data={stats()?.usage ?? []} />
<ModelHero
data={stats() ?? null}
catalog={catalogEntry() ?? null}
catalogData={catalog() ?? null}
labName={labName()}
/>
<ModelOverview catalog={catalogEntry() ?? null} />
<ModelMomentumSection data={stats() ?? null} />
<ModelEfficiencySection data={stats() ?? null} catalog={catalogEntry() ?? null} />
<ModelGeoBreakdownSection data={stats()?.country ?? emptyCountryRecord()} />
<ModelPeersSection data={stats() ?? null} />
@@ -249,128 +254,227 @@ function ModelNotFound(props: { lab: string; model: string }) {
)
}
function ModelHero(props: { data: StatsModelData | null; catalog: ModelCatalogEntry | null; labName: string }) {
function ModelHero(props: {
data: StatsModelData | null
catalog: ModelCatalogEntry | null
catalogData: ModelCatalog | null
labName: string
}) {
const i18n = useI18n()
const language = useLanguage()
const labId = () => props.catalog?.lab ?? props.data?.provider ?? props.labName
const modelId = () => props.catalog?.id ?? props.data?.model ?? i18n.t("model.fallback")
const modelName = () => props.catalog?.name ?? props.data?.model ?? i18n.t("model.fallback")
const weights = () => props.catalog?.weights[0]
const labs = () => props.catalogData?.labs ?? []
const labModels = () =>
props.catalogData?.labs.find((lab) => lab.id === providerSlug(labId()))?.models ??
(props.catalog ? [props.catalog] : [])
return (
<section id="overview" data-section="model-hero">
<a data-slot="model-back-link" href={language.route(import.meta.env.BASE_URL)}>
{i18n.t("footer.modelData")}
</a>
<div data-slot="model-hero-grid">
<div data-slot="model-hero-copy">
<div data-slot="model-hero-tags">
<a data-slot="hero-meta" href={language.route(`${import.meta.env.BASE_URL}${providerSlug(labId())}`)}>
<ProviderIcon aria-hidden="true" id={getProviderIconId(labId())} />
<nav data-component="model-hero-breadcrumb" aria-label="Data breadcrumb">
<a data-slot="model-hero-crumb" href={language.route(import.meta.env.BASE_URL)}>
Data
</a>
<span data-slot="model-hero-separator">/</span>
<Show
when={labs().length > 0}
fallback={
<span data-slot="model-hero-crumb" data-current="true">
<span>{props.labName}</span>
</a>
<span data-slot="model-id-tag">{modelId()}</span>
</div>
<h1>
<a data-slot="heading-link" href="#overview">
{props.catalog?.name ?? props.data?.model ?? i18n.t("model.fallback")}
</a>
</h1>
<Show when={props.data} fallback={<p>{i18n.t("model.catalogFallback")}</p>}>
{(data) => (
<p>
{data().rank === null ? i18n.t("model.unranked") : i18n.t("model.ranked", { rank: data().rank ?? "" })}{" "}
{i18n.t("model.observedVolume", { share: formatPercent(data().tokenShare) })}
</p>
)}
</Show>
<ChevronDownIcon />
</span>
}
>
<details data-component="model-hero-menu">
<summary data-slot="model-hero-crumb" data-current="true">
<span>{props.labName}</span>
<ChevronDownIcon />
</summary>
<div data-slot="model-hero-options">
<For each={labs()}>
{(lab) => (
<a
data-slot="model-hero-option"
data-current={lab.id === providerSlug(labId()) ? "true" : undefined}
href={language.route(`${import.meta.env.BASE_URL}${lab.id}`)}
>
{lab.name}
</a>
)}
</For>
</div>
</details>
</Show>
<span data-slot="model-hero-separator">/</span>
<Show
when={labModels().length > 0}
fallback={
<span data-slot="model-hero-crumb" data-current="true" aria-current="page">
<span>{modelName()}</span>
<ChevronDownIcon />
</span>
}
>
<details data-component="model-hero-menu">
<summary data-slot="model-hero-crumb" data-current="true" aria-current="page">
<span>{modelName()}</span>
<ChevronDownIcon />
</summary>
<div data-slot="model-hero-options">
<For each={labModels()}>
{(model) => (
<a
data-slot="model-hero-option"
data-current={model.id === props.catalog?.id ? "true" : undefined}
href={language.route(`${import.meta.env.BASE_URL}${model.id}`)}
>
{model.name}
</a>
)}
</For>
</div>
</details>
</Show>
</nav>
<div data-slot="model-hero-title-row">
<span data-slot="model-hero-avatar">
<ProviderIcon aria-hidden="true" id={getProviderIconId(labId())} />
</span>
<h1>{modelName()}</h1>
<div data-slot="model-hero-actions">
<Show when={props.catalog?.openWeights && weights()}>
{(weight) => (
<a data-slot="model-weight-link" href={weight().url} target="_blank" rel="noopener noreferrer">
{i18n.t("model.weights", { label: weight().label })}
<a data-slot="model-hero-action" href={weight().url} target="_blank" rel="noopener noreferrer">
<ModelHeroActionIcon kind="weights" />
<span>Model weights</span>
</a>
)}
</Show>
</div>
<Show when={props.data} fallback={<ModelCatalogCallout catalog={props.catalog} />}>
{(data) => (
<div data-component="model-rank-panel">
<span>{i18n.t("model.rank")}</span>
<strong>{data().rank === null ? "—" : `#${data().rank}`}</strong>
<p>{formatModelRankMoveLabel(data(), i18n)}</p>
</div>
)}
</Show>
</div>
<div data-slot="model-hero-pattern" aria-hidden="true" />
<Show when={props.catalog}>{(catalog) => <ModelCatalogPanel data={catalog()} />}</Show>
<Show
when={props.data}
fallback={
<p data-slot="model-hero-state">
<span>Listed</span>
<span>across the shared model catalog.</span>
</p>
}
>
{(data) => (
<p data-slot="model-hero-rankline">
<span>Ranked</span>
<span data-slot="model-hero-rank-group">
<span data-slot="model-hero-pill">{formatHeroRank(data().rank)}</span>
<ModelHeroSparkline data={data()} />
</span>
<span>across last week's</span>
<span data-slot="model-hero-pill">OpenCode Go</span>
<span>usage with</span>
<span data-slot="model-hero-pill">{formatPercent(data().tokenShare)}</span>
<span>of observed</span>
<span data-slot="model-hero-pill">2M</span>
<span>volume.</span>
</p>
)}
</Show>
</section>
)
}
function ModelCatalogCallout(props: { catalog: ModelCatalogEntry | null }) {
function ModelHeroActionIcon(props: { kind: "weights" | "compare" }) {
if (props.kind === "weights")
return (
<svg data-slot="model-hero-action-icon" viewBox="0 0 16 16" aria-hidden="true" fill="none">
<path d="M5.5 4.5H4.5V11.5H11.5V10.5" stroke="currentColor" stroke-linecap="square" />
<path d="M8.5 4.5H11.5V7.5" stroke="currentColor" stroke-linecap="square" />
<path d="M11.25 4.75L7.25 8.75" stroke="currentColor" stroke-linecap="square" />
</svg>
)
return (
<svg data-slot="model-hero-action-icon" viewBox="0 0 16 16" aria-hidden="true" fill="none">
<rect x="3.5" y="3.5" width="3" height="3" stroke="currentColor" />
<rect x="9.5" y="3.5" width="3" height="3" stroke="currentColor" />
<rect x="3.5" y="9.5" width="3" height="3" stroke="currentColor" />
<rect x="9.5" y="9.5" width="3" height="3" stroke="currentColor" />
</svg>
)
}
function ModelHeroSparkline(props: { data: StatsModelData }) {
const values = () => props.data.usage.slice(-14).map((point) => point.tokens)
return (
<span data-slot="model-hero-sparkline" aria-hidden="true">
<svg viewBox="0 0 36 24" fill="none">
<path d={sparklineAreaPath(values())} fill="currentColor" opacity="0.14" />
<path d={sparklineLinePath(values())} stroke="currentColor" stroke-width="1.5" stroke-linejoin="round" />
</svg>
</span>
)
}
function ChevronDownIcon() {
return (
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true" fill="none">
<path d="M4.75 6.25L8 9.5L11.25 6.25" stroke="currentColor" stroke-width="1.5" />
</svg>
)
}
function ModelOverview(props: { catalog: ModelCatalogEntry | null }) {
const i18n = useI18n()
const language = useLanguage()
const specs = createMemo(() => [
{
label: i18n.t("model.context"),
value: formatCatalogLimit(props.catalog?.limit?.context, i18n.t("home.unknown")),
},
{
label: i18n.t("model.output"),
value: formatCatalogLimit(props.catalog?.limit?.output, i18n.t("home.unknown")),
},
{
label: i18n.t("model.knowledge"),
value: formatCatalogMonth(props.catalog?.knowledge, language.tag(language.locale()), i18n.t("home.unknown")),
},
{
label: i18n.t("model.release"),
value: formatCatalogMonth(props.catalog?.releaseDate, language.tag(language.locale()), i18n.t("home.unknown")),
},
{
label: i18n.t("model.inputs"),
value: formatCatalogModalities(
props.catalog?.modalities.input ?? [],
language.tag(language.locale()),
i18n.t("home.unknown"),
),
},
])
return (
<section id="model-overview" data-section="model-specs" aria-label={i18n.t("nav.overview")}>
<For each={specs()}>
{(spec) => (
<div data-component="model-spec">
<span>{spec.label}</span>
<strong>{spec.value}</strong>
</div>
)}
</For>
</section>
)
}
function ModelMomentumSection(props: { data: StatsModelData | null }) {
const i18n = useI18n()
const language = useLanguage()
return (
<div data-component="model-rank-panel">
<span>{i18n.t("model.profile")}</span>
<strong>
{props.catalog?.releaseDate
? formatCatalogDate(props.catalog.releaseDate, language.tag(language.locale()), i18n.t("home.unknown"))
: i18n.t("model.listed")}
</strong>
<p>{i18n.t("model.noCurrentUsage")}</p>
</div>
)
}
function ModelCatalogPanel(props: { data: ModelCatalogEntry }) {
const i18n = useI18n()
const language = useLanguage()
return (
<aside data-component="model-catalog" aria-label={i18n.t("model.facts")}>
<div data-slot="model-catalog-grid">
<CatalogDatum
label={i18n.t("model.context")}
value={formatCatalogLimit(props.data.limit?.context, i18n.t("home.unknown"))}
/>
<CatalogDatum
label={i18n.t("model.output")}
value={formatCatalogLimit(props.data.limit?.output, i18n.t("home.unknown"))}
/>
<CatalogDatum
label={i18n.t("model.knowledge")}
value={formatCatalogDate(props.data.knowledge, language.tag(language.locale()), i18n.t("home.unknown"))}
/>
<CatalogDatum
label={i18n.t("model.release")}
value={formatCatalogDate(props.data.releaseDate, language.tag(language.locale()), i18n.t("home.unknown"))}
/>
<CatalogDatum
label={i18n.t("model.inputs")}
value={formatCatalogModalities(props.data.modalities.input, i18n)}
/>
</div>
</aside>
)
}
function CatalogDatum(props: { label: string; value: string }) {
return (
<article data-component="model-catalog-datum">
<span>{props.label}</span>
<strong>{props.value}</strong>
</article>
)
}
function ModelOverview(props: { data: StatsModelData | null }) {
const i18n = useI18n()
return (
<section id="model-overview" data-section="model-panel">
<SectionTitle
href="#model-overview"
title={i18n.t("nav.overview")}
description={i18n.t("model.overviewDescription")}
/>
<section id="momentum" data-section="model-momentum">
<h2 data-slot="model-momentum-title">
<a href="#momentum">{i18n.t("model.momentum")}.</a>
<span>{i18n.t("model.overviewDescription")}</span>
</h2>
<div data-slot="model-momentum-pattern" aria-hidden="true" />
<Show
when={props.data}
fallback={
@@ -378,189 +482,83 @@ function ModelOverview(props: { data: StatsModelData | null }) {
}
>
{(data) => (
<div data-component="model-metric-grid">
<MetricCard
label={i18n.t("model.tokens")}
value={formatTokens(data().totals.tokens)}
detail={i18n.t("model.lastTwoMonths")}
/>
<MetricCard
label={i18n.t("model.uniqueUsers")}
value={formatUsers(data().totals.uniqueUsers)}
detail={i18n.t("model.lastTwoMonths")}
/>
<MetricCard
label={i18n.t("model.sessions")}
value={formatInteger(data().totals.sessions)}
detail={i18n.t("model.completedSessions")}
/>
<MetricCard
label={i18n.t("model.tokenShare")}
value={formatPercent(data().tokenShare)}
detail={i18n.t("model.totalModels", { count: data().totalModels })}
/>
<MetricCard
label={i18n.t("model.momentum")}
value={formatChange(data().tokenChange)}
detail={i18n.t("model.vsPreviousWindow")}
state={data().tokenChange < 0 ? "negative" : "positive"}
/>
</div>
<>
<MomentumChart data={data()} locale={language.tag(language.locale())} />
<div data-slot="model-momentum-metrics">
<MomentumMetric label={i18n.t("model.uniqueUsers")} value={formatUsers(data().totals.uniqueUsers)} />
<MomentumMetric
label={capitalizeLabel(i18n.t("model.completedSessions"))}
value={formatInteger(data().totals.sessions)}
/>
<MomentumMetric label={i18n.t("model.tokenShare")} value={formatPercent(data().tokenShare)} />
<MomentumMetric
label="Rank"
value={formatRankLabel(data().rank)}
watermark={formatRankLabel(data().rank)}
/>
</div>
</>
)}
</Show>
</section>
)
}
function ModelUsageSection(props: { data: ModelUsagePoint[] }) {
const i18n = useI18n()
function MomentumChart(props: { data: StatsModelData; locale: string }) {
const chart = createMemo(() => momentumChart(props.data.usage, props.data.updatedAt))
const changeState = createMemo(() => (props.data.tokenChange < 0 ? "negative" : "positive"))
return (
<section id="usage" data-section="model-panel">
<SectionTitle href="#usage" title={i18n.t("nav.usage")} description={i18n.t("model.usageDescription")} />
<Show
when={props.data.some((item) => item.tokens > 0)}
fallback={
<ModelEmptyState title={i18n.t("model.noUsageTitle")} description={i18n.t("model.noUsageDescription")} />
}
>
<ModelColumnChart data={props.data} metric="tokens" ariaLabel={i18n.t("model.dailyTokenChart")} />
</Show>
</section>
)
}
function ModelUsersSection(props: { data: ModelUsagePoint[] }) {
const i18n = useI18n()
return (
<section id="users" data-section="model-panel">
<SectionTitle href="#users" title={i18n.t("model.uniqueUsers")} description={i18n.t("model.usersDescription")} />
<Show
when={props.data.some((item) => item.users > 0)}
fallback={
<ModelEmptyState title={i18n.t("model.noUsersTitle")} description={i18n.t("model.noUsersDescription")} />
}
>
<ModelColumnChart data={props.data} metric="users" ariaLabel={i18n.t("model.dailyUserChart")} />
</Show>
</section>
)
}
function ModelColumnChart(props: { data: ModelUsagePoint[]; metric: "tokens" | "users"; ariaLabel: string }) {
const i18n = useI18n()
const [activeIndex, setActiveIndex] = createSignal<number>()
const max = createMemo(() => Math.max(0, ...props.data.map((item) => modelUsageMetricValue(item, props.metric))) || 1)
const activePoint = createMemo(() => {
const index = activeIndex()
if (index === undefined) return undefined
return props.data[index]
})
return (
<div
data-component="model-usage-chart"
data-metric={props.metric}
data-dense-labels={isModelUsageDense(props.data.length) ? "true" : undefined}
role="img"
aria-label={props.ariaLabel}
style={{ "--model-usage-count": props.data.length } as JSX.CSSProperties}
onPointerLeave={(event) => {
if (event.pointerType === "touch") return
setActiveIndex(undefined)
}}
>
<div data-slot="model-usage-axis" aria-hidden="true">
<For each={props.data}>
{(point, index) => (
<div
data-active={activeIndex() === index() ? "true" : undefined}
data-label-hidden={isModelUsageLabelHidden(index(), props.data.length) ? "true" : undefined}
>
<span data-slot="model-usage-label">
<span data-slot="model-usage-total">{formatModelUsageValue(point, props.metric)}</span>
<span data-slot="model-usage-date">{point.date}</span>
</span>
</div>
)}
</For>
<div data-component="model-momentum-chart" role="img" aria-label="Recent model token momentum">
<div data-slot="model-momentum-summary">
<div data-slot="model-momentum-total">
<strong>{formatTokens(props.data.totals.tokens)} tokens</strong>
<span data-state={changeState()}>{formatChange(props.data.tokenChange)}</span>
</div>
<p>
<span>{formatMomentumDate(chart().startDate, props.locale, props.data.updatedAt)}</span>
<span aria-hidden="true"></span>
<span>{formatMomentumDate(chart().endDate, props.locale, props.data.updatedAt)}</span>
</p>
</div>
<div data-slot="model-usage-bars">
<For each={props.data}>
{(point, index) => (
<div
data-slot="model-usage-column"
role="button"
tabIndex={0}
aria-label={`${point.date} ${formatModelUsageValue(point, props.metric)} ${modelUsageLabel(props.metric, i18n)}`}
data-active={activeIndex() === index() ? "true" : undefined}
data-muted={activeIndex() !== undefined && activeIndex() !== index() ? "true" : undefined}
onPointerDown={(event) => {
if (event.pointerType !== "touch") return
setActiveIndex(index())
}}
onPointerEnter={() => setActiveIndex(index())}
onPointerMove={(event) => {
if (event.pointerType === "touch") return
setActiveIndex(index())
}}
onClick={() => setActiveIndex(index())}
onFocus={() => setActiveIndex(index())}
onBlur={() => setActiveIndex(undefined)}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return
event.preventDefault()
setActiveIndex(index())
}}
>
<div
data-slot="model-usage-bar"
style={
{
"--model-usage-fill": `${modelUsageHeight(modelUsageMetricValue(point, props.metric), max())}%`,
} as JSX.CSSProperties
}
<div data-slot="model-momentum-plot">
<svg viewBox="0 0 1200 370" preserveAspectRatio="none" aria-hidden="true">
<path data-slot="model-momentum-line-muted" d={chart().previousPath} />
<path data-slot="model-momentum-line-active" d={chart().currentPath} />
<For each={chart().markers}>
{(marker) => (
<rect
data-slot="model-momentum-marker"
data-active={marker.active ? "true" : undefined}
x={marker.x - 3}
y={marker.y - 3}
width="6"
height="6"
/>
<Show when={activeIndex() === index() && activePoint()}>
{(active) => (
<div
data-component="chart-tooltip"
data-placement={index() > props.data.length * 0.62 ? "left" : "right"}
>
<strong>{active().date}</strong>
<span>
{formatModelUsageValue(active(), props.metric)} {modelUsageLabel(props.metric, i18n)}
</span>
<div data-slot="tooltip-divider" />
<p>
<span data-slot="tooltip-label">
<i /> {i18n.t("chart.daily")} {modelUsageLabel(props.metric, i18n)}
</span>
<b>{formatModelUsageValue(active(), props.metric)}</b>
</p>
</div>
)}
</Show>
</div>
)}
)}
</For>
</svg>
<span data-slot="model-momentum-end" data-state={changeState()} style={chart().endStyle}>
<i />
{formatChange(props.data.tokenChange)}
</span>
</div>
<div data-slot="model-momentum-months" aria-hidden="true">
<For each={momentumMonthLabels(chart().startDate, props.locale, props.data.updatedAt)}>
{(month) => <span style={{ left: `${month.x}%` }}>{month.label}</span>}
</For>
</div>
</div>
)
}
function modelUsageMetricValue(point: ModelUsagePoint, metric: "tokens" | "users") {
if (metric === "users") return point.users
return point.tokens
}
function formatModelUsageValue(point: ModelUsagePoint, metric: "tokens" | "users") {
if (metric === "users") return formatUsers(point.users)
return formatTokens(point.tokens)
}
function modelUsageLabel(metric: "tokens" | "users", i18n: ReturnType<typeof useI18n>) {
if (metric === "users") return i18n.t("format.users")
return i18n.t("format.tokens")
function MomentumMetric(props: { label: string; value: string; watermark?: string }) {
return (
<div data-component="model-momentum-metric">
<Show when={props.watermark}>{(watermark) => <em aria-hidden="true">{watermark()}</em>}</Show>
<span>{props.label}</span>
<strong>{props.value}</strong>
</div>
)
}
function ModelEfficiencySection(props: { data: StatsModelData | null; catalog: ModelCatalogEntry | null }) {
@@ -918,19 +916,111 @@ function formatGeoShare(value: number) {
return `${value.toFixed(value > 0 && value < 1 ? 1 : 0)}%`
}
function modelUsageHeight(tokens: number, max: number) {
if (tokens <= 0) return 0
return Math.max(2, Math.min(100, (tokens / max) * 100))
function momentumChart(data: ModelUsagePoint[], updatedAt: string | null) {
const fallbackDate = updatedAt ? formatMomentumDateLabel(updatedAt) : "JAN 1"
const points =
data.length > 1 ? data : [data[0] ?? emptyUsagePoint(fallbackDate), data[0] ?? emptyUsagePoint(fallbackDate)]
const max = Math.max(1, ...points.map((point) => point.tokens))
const split = Math.max(1, Math.floor((points.length - 1) / 2))
const coordinates = points.map((point, index) => ({
date: point.date,
tokens: point.tokens,
x: (index / Math.max(1, points.length - 1)) * 1200,
y: 364 - (point.tokens / max) * 364,
}))
const end = coordinates[coordinates.length - 1]
return {
startDate: coordinates[0].date,
endDate: end.date,
previousPath: smoothLinePath(coordinates.slice(0, split + 1)),
currentPath: smoothLinePath(coordinates.slice(split)),
markers: [
{ ...coordinates[0], active: false },
{ ...coordinates[split], active: true },
{ ...end, active: true },
],
endStyle: {
"--momentum-end-x": `${Math.min(94, Math.max(0, ((end.x + 8) / 1200) * 100))}%`,
"--momentum-end-y": `${Math.min(96, Math.max(0, ((end.y - 7) / 370) * 100))}%`,
} as JSX.CSSProperties,
}
}
function isModelUsageDense(count: number) {
return count > 20
function emptyUsagePoint(date: string): ModelUsagePoint {
return { date, tokens: 0, users: 0, sessions: 0, cost: 0 }
}
function isModelUsageLabelHidden(index: number, count: number) {
if (count <= 16) return false
const interval = Math.ceil(count / 8)
return index !== count - 1 && index % interval !== 0
function smoothLinePath(points: { x: number; y: number }[]) {
if (points.length === 0) return ""
if (points.length === 1) return `M${formatSparklinePoint(points[0].x)} ${formatSparklinePoint(points[0].y)}`
return points
.map((point, index) => {
if (index === 0) return `M${formatSparklinePoint(point.x)} ${formatSparklinePoint(point.y)}`
const previous = points[index - 1]
const next = points[index + 1] ?? point
const beforePrevious = points[index - 2] ?? previous
const controlStart = {
x: previous.x + (point.x - beforePrevious.x) / 6,
y: previous.y + (point.y - beforePrevious.y) / 6,
}
const controlEnd = {
x: point.x - (next.x - previous.x) / 6,
y: point.y - (next.y - previous.y) / 6,
}
return `C${formatSparklinePoint(controlStart.x)} ${formatSparklinePoint(controlStart.y)} ${formatSparklinePoint(controlEnd.x)} ${formatSparklinePoint(controlEnd.y)} ${formatSparklinePoint(point.x)} ${formatSparklinePoint(point.y)}`
})
.join(" ")
}
function momentumMonthLabels(startDate: string, locale: string, updatedAt: string | null) {
const start = parseMomentumDate(startDate, updatedAt)
const first = new Date(Date.UTC(start.getUTCFullYear(), start.getUTCMonth() - 1, 1))
return Array.from({ length: 5 }, (_, index) => {
const date = new Date(Date.UTC(first.getUTCFullYear(), first.getUTCMonth() + index, 1))
return {
label: new Intl.DateTimeFormat(locale, { month: "short", timeZone: "UTC" }).format(date).toUpperCase(),
x: index === 4 ? 98 : index * 24.5,
}
})
}
function formatMomentumDate(date: string, locale: string, updatedAt: string | null) {
return new Intl.DateTimeFormat(locale, {
month: "short",
day: "numeric",
year: "numeric",
timeZone: "UTC",
})
.format(parseMomentumDate(date, updatedAt))
.toUpperCase()
}
function parseMomentumDate(date: string, updatedAt: string | null): Date {
const iso = /^(\d{4})-(\d{2})-(\d{2})/.exec(date)
if (iso) return new Date(Date.UTC(Number(iso[1]), Number(iso[2]) - 1, Number(iso[3])))
const label = /^([A-Za-z]{3})\s+(\d{1,2})$/.exec(date.trim())
const month = label ? shortMonths.findIndex((item) => item.toLowerCase() === label[1].toLowerCase()) : -1
if (!label || month < 0) return new Date(Date.UTC(1970, 0, 1))
const anchor = updatedAt ? parseMomentumDate(updatedAt, null) : new Date(Date.UTC(1970, 0, 1))
const year = month > anchor.getUTCMonth() + 1 ? anchor.getUTCFullYear() - 1 : anchor.getUTCFullYear()
return new Date(Date.UTC(year, month, Number(label[2])))
}
function formatMomentumDateLabel(date: string) {
const parsed = parseMomentumDate(date, null)
if (parsed.getUTCFullYear() === 1970) return "JAN 1"
return `${shortMonths[parsed.getUTCMonth()]} ${parsed.getUTCDate()}`
}
function formatRankLabel(rank: number | null) {
if (rank === null) return "--"
return `#${String(rank).padStart(2, "0")}`
}
function capitalizeLabel(value: string) {
return value.charAt(0).toUpperCase() + value.slice(1)
}
function formatRankMove(change: number) {
@@ -938,6 +1028,40 @@ function formatRankMove(change: number) {
return `${change}`
}
function formatHeroRank(rank: number | null) {
if (rank === null) return "--"
return String(rank).padStart(2, "0")
}
function sparklineLinePath(values: number[]) {
return sparklinePoints(values)
.map(
(point, index) => `${index === 0 ? "M" : "L"}${formatSparklinePoint(point.x)} ${formatSparklinePoint(point.y)}`,
)
.join(" ")
}
function sparklineAreaPath(values: number[]) {
const points = sparklinePoints(values)
return `M${formatSparklinePoint(points[0].x)} 18 ${points
.map((point) => `L${formatSparklinePoint(point.x)} ${formatSparklinePoint(point.y)}`)
.join(" ")} L${formatSparklinePoint(points[points.length - 1].x)} 18 Z`
}
function sparklinePoints(values: number[]) {
const normalized = values.length > 1 ? values : [values[0] ?? 0, values[0] ?? 0]
const min = Math.min(...normalized)
const max = Math.max(...normalized)
return normalized.map((value, index) => ({
x: 8 + (index / Math.max(1, normalized.length - 1)) * 20,
y: min === max ? 12 : 18 - ((value - min) / (max - min)) * 12,
}))
}
function formatSparklinePoint(value: number) {
return Number(value.toFixed(2)).toString()
}
function formatModelRankMoveLabel(data: StatsModelData, i18n: ReturnType<typeof useI18n>) {
if (data.rank === null) return i18n.t("model.noUsageLastWeek")
if (data.previousRank === null) return i18n.t("model.newThisWeek")
@@ -955,6 +1079,28 @@ function formatTokens(value: number) {
return String(Math.round(value))
}
function formatCatalogLimit(value: number | undefined, unknown: string) {
return value === undefined ? unknown : formatTokens(value)
}
function formatCatalogMonth(value: string | undefined, locale: string, unknown: string) {
if (!value) return unknown
const match = /^(\d{4})(?:-(\d{2}))?(?:-(\d{2}))?$/.exec(value)
if (!match) return value
return new Intl.DateTimeFormat(locale, {
month: match[2] ? "short" : undefined,
year: "numeric",
timeZone: "UTC",
}).format(new Date(Date.UTC(Number(match[1]), match[2] ? Number(match[2]) - 1 : 0, 1)))
}
function formatCatalogModalities(values: string[], locale: string, unknown: string) {
if (values.length === 0) return unknown
const labels = values.map((value) => value.replace(/[-_]/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase()))
if (labels.length === 1) return labels[0] ?? unknown
return new Intl.ListFormat(locale, { style: "long", type: "conjunction" }).format(labels)
}
function formatInteger(value: number) {
return new Intl.NumberFormat("en").format(value)
}
@@ -993,35 +1139,6 @@ function formatChange(value: number) {
return `${value}%`
}
function formatCatalogLimit(value: number | undefined, unknown: string) {
return value === undefined ? unknown : formatTokens(value)
}
function formatCatalogModalities(value: string[], i18n: ReturnType<typeof useI18n>) {
if (value.length === 0) return i18n.t("home.unknown")
return value.map((item) => formatCatalogModality(item, i18n)).join(", ")
}
function formatCatalogModality(value: string, i18n: ReturnType<typeof useI18n>) {
if (value === "pdf") return i18n.t("model.pdf")
return value.charAt(0).toUpperCase() + value.slice(1)
}
function formatCatalogDate(value: string | undefined, locale: string, unknown: string) {
if (!value) return unknown
const match = /^(\d{4})(?:-(\d{2}))?(?:-(\d{2}))?$/.exec(value)
if (!match) return value
const year = Number(match[1])
const month = match[2] ? Number(match[2]) - 1 : 0
const day = match[3] ? Number(match[3]) : 1
return new Intl.DateTimeFormat(locale, {
month: match[2] ? "short" : undefined,
day: match[3] ? "numeric" : undefined,
year: "numeric",
timeZone: "UTC",
}).format(new Date(Date.UTC(year, month, day)))
}
function trimNumber(value: number, digits: number) {
return Number(value.toFixed(digits)).toLocaleString("en")
}
+668 -189
View File
@@ -1,19 +1,24 @@
import "../index.css"
import { Meta, Title } from "@solidjs/meta"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import {
getStatsLabData,
getStatsHomeData,
type LabUsageModelEntry,
type MarketDay,
type ModelUsagePoint,
type StatsHomeData,
type StatsLabData,
} from "@opencode-ai/stats-core/domain/home"
import { createAsync, query, useParams } from "@solidjs/router"
import { createMemo, createSignal, For, onMount, Show, type JSX } from "solid-js"
import { createMemo, createSignal, createUniqueId, For, onMount, Show, type JSX } from "solid-js"
import { getRequestEvent } from "solid-js/web"
import { LocaleLinks } from "../../component/locale-links"
import { useI18n } from "../../context/i18n"
import { useLanguage } from "../../context/language"
import { localizedUrl } from "../../lib/language"
import {
catalogSlug,
findModelCatalogLab,
formatCatalogLabName,
getModelCatalog,
@@ -41,6 +46,19 @@ const getLabData = query(async (lab: string) => {
return runStatsEffect(getStatsLabData(lab))
}, "getStatsLabData")
const getHomeData = query(async () => {
"use server"
return runStatsEffect(getStatsHomeData())
}, "getStatsHomeData")
type LabModelTooltipState = {
model: ModelCatalogEntry
placement: "left" | "right"
usage: LabUsageModelEntry | undefined
x: number
y: number
}
export default function StatsLab() {
const i18n = useI18n()
const language = useLanguage()
@@ -60,6 +78,7 @@ export default function StatsLab() {
if (!entry) return Promise.resolve(null)
return getLabData(entry.id)
})
const homeStats = createAsync((): Promise<StatsHomeData | undefined> => getHomeData())
const githubStars = createAsync(() => getGitHubStars())
const [themePreference, setThemePreference] = createSignal<ThemePreference>("system")
const labName = createMemo(() => lab()?.name ?? formatCatalogLabName(labParam()))
@@ -118,12 +137,18 @@ export default function StatsLab() {
<div data-component="container">
<div data-component="content">
<Show when={catalog() !== undefined} fallback={<LabLoading />}>
<Show when={lab()} fallback={<LabNotFound lab={labParam()} />}>
<Show when={lab()} fallback={<LabNotFound lab={labParam()} labs={catalog()?.labs ?? []} />}>
{(data) => (
<>
<LabHero lab={data()} stats={stats() ?? null} />
<LabHero lab={data()} labs={catalog()?.labs ?? []} />
<LabOverview lab={data()} data={stats() ?? null} />
<LabUsageSection lab={data()} data={stats() ?? null} />
<LabModelsSection lab={data()} usage={stats()?.models ?? []} />
<LabRelatedSection
lab={data()}
labs={catalog()?.labs ?? []}
market={homeStats()?.market["2M"] ?? []}
/>
</>
)}
</Show>
@@ -141,201 +166,329 @@ export default function StatsLab() {
function LabLoading() {
const i18n = useI18n()
const language = useLanguage()
return (
<section id="overview" data-section="lab-hero">
<div data-slot="model-hero-grid">
<div data-slot="model-hero-copy">
<a data-slot="model-back-link" href={language.route(import.meta.env.BASE_URL)}>
{i18n.t("footer.modelData")}
</a>
<h1>
<a data-slot="heading-link" href="#overview">
{i18n.t("lab.loadingTitle")}
</a>
</h1>
<p>{i18n.t("lab.loadingDescription")}</p>
</div>
</div>
<LabHeroBreadcrumb label={i18n.t("lab.loadingTitle")} />
<LabHeroTitleRow label={i18n.t("lab.loadingTitle")} />
</section>
)
}
function LabNotFound(props: { lab: string }) {
function LabNotFound(props: { lab: string; labs: ModelCatalogLab[] }) {
const i18n = useI18n()
const language = useLanguage()
const labName = () => formatCatalogLabName(props.lab)
return (
<section id="overview" data-section="lab-hero">
<div data-slot="model-hero-grid">
<div data-slot="model-hero-copy">
<a data-slot="model-back-link" href={language.route(import.meta.env.BASE_URL)}>
{i18n.t("footer.modelData")}
</a>
<h1>
<a data-slot="heading-link" href="#overview">
{formatCatalogLabName(props.lab)}
</a>
</h1>
<p>{i18n.t("lab.notFound")}</p>
</div>
</div>
<LabHeroBreadcrumb label={labName()} labs={props.labs} />
<LabHeroTitleRow label={labName()} />
<p data-slot="lab-hero-state">{i18n.t("lab.notFound")}</p>
</section>
)
}
function LabHero(props: { lab: ModelCatalogLab; stats: StatsLabData | null }) {
const i18n = useI18n()
const language = useLanguage()
const latest = createMemo(
() =>
props.lab.models
.map((model) => model.releaseDate)
.filter((value): value is string => value !== undefined)
.toSorted((a, b) => new Date(b).getTime() - new Date(a).getTime())[0],
)
const featuredModels = createMemo(() => props.lab.models.slice(0, 3).map((model) => model.name))
function LabHero(props: { lab: ModelCatalogLab; labs: ModelCatalogLab[] }) {
return (
<section id="overview" data-section="lab-hero">
<a data-slot="model-back-link" href={language.route(import.meta.env.BASE_URL)}>
{i18n.t("footer.modelData")}
<LabHeroBreadcrumb label={props.lab.name} labs={props.labs} />
<LabHeroTitleRow icon={props.lab.id} label={props.lab.name} />
</section>
)
}
function LabHeroBreadcrumb(props: { label: string; labs?: ModelCatalogLab[] }) {
const language = useLanguage()
const labs = () => props.labs ?? []
return (
<nav data-component="lab-hero-breadcrumb" aria-label="Data breadcrumb">
<a data-slot="lab-hero-crumb" href={language.route(import.meta.env.BASE_URL)}>
Data
</a>
<div data-slot="model-hero-grid">
<div data-slot="model-hero-copy">
<h1>
<a data-slot="heading-link" href="#overview">
{props.lab.name}
</a>
</h1>
<div data-slot="model-hero-pattern" aria-hidden="true" />
<p>
{i18n.t("lab.heroPrefix", { count: props.lab.models.length, lab: props.lab.name })}
<Show when={featuredModels().length > 0}>
{" "}
{i18n.t("lab.heroIncluding", { models: formatList(featuredModels(), language.tag(language.locale())) })}
</Show>
. {i18n.t("lab.heroSuffix")}
</p>
</div>
<div data-component="model-rank-panel">
<span>{i18n.t("lab.tokensProcessed")}</span>
<strong>{props.stats ? formatTokens(props.stats.totals.tokens) : i18n.t("lab.pending")}</strong>
<p>
{props.stats
? i18n.t("lab.shareOfUsage", { share: formatPercent(props.stats.tokenShare) })
: latest()
? i18n.t("lab.latestRelease", {
date: formatCatalogDate(latest(), language.tag(language.locale()), i18n.t("home.unknown")),
})
: i18n.t("lab.usageAfterActivity")}
</p>
</div>
<span data-slot="lab-hero-separator">/</span>
<Show
when={labs().length > 0}
fallback={
<span data-slot="lab-hero-crumb" data-current="true" aria-current="page">
<span>{props.label}</span>
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
<path d="M4.75 6.25L8 9.5L11.25 6.25" fill="none" stroke="currentColor" stroke-width="1.5" />
</svg>
</span>
}
>
<details data-component="lab-hero-menu">
<summary data-slot="lab-hero-crumb" data-current="true" aria-current="page">
<span>{props.label}</span>
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
<path d="M4.75 6.25L8 9.5L11.25 6.25" fill="none" stroke="currentColor" stroke-width="1.5" />
</svg>
</summary>
<div data-slot="lab-hero-options">
<For each={labs()}>
{(lab) => (
<a
data-slot="lab-hero-option"
data-current={lab.name === props.label ? "true" : undefined}
href={language.route(`${import.meta.env.BASE_URL}${lab.id}`)}
>
{lab.name}
</a>
)}
</For>
</div>
</details>
</Show>
</nav>
)
}
function LabHeroTitleRow(props: { icon?: string; label: string }) {
return (
<div data-slot="lab-hero-title-row">
<span data-slot="lab-hero-avatar" data-empty={props.icon ? undefined : "true"}>
<Show when={props.icon}>{(icon) => <ProviderIcon aria-hidden="true" id={getProviderIconId(icon())} />}</Show>
</span>
<h1>{props.label}</h1>
<div data-slot="lab-hero-pattern" aria-hidden="true" />
</div>
)
}
function LabOverview(props: { lab: ModelCatalogLab; data: StatsLabData | null }) {
const i18n = useI18n()
const language = useLanguage()
const featuredModels = createMemo(() => props.lab.models.slice(0, 3).map((model) => model.name))
return (
<section data-section="lab-overview">
<div data-slot="lab-overview-copy">
<ProviderIcon data-slot="lab-overview-watermark" aria-hidden="true" id={getProviderIconId(props.lab.id)} />
<p>
{i18n.t("lab.heroPrefix", { count: props.lab.models.length, lab: props.lab.name })}
<Show when={featuredModels().length > 0}>
{" "}
{i18n.t("lab.heroIncluding", { models: formatList(featuredModels(), language.tag(language.locale())) })}
</Show>
. {i18n.t("lab.heroSuffix")}
</p>
</div>
<LabOverviewMetric
label="Tokens processed"
value={props.data ? formatTokens(props.data.totals.tokens) : i18n.t("lab.pending")}
/>
<LabOverviewMetric
label="Recent usage"
value={props.data ? formatWholePercent(props.data.tokenShare) : i18n.t("lab.pending")}
/>
</section>
)
}
function LabOverviewMetric(props: { label: string; value: string }) {
return (
<article data-component="lab-overview-metric">
<span>{props.label}</span>
<strong>{props.value}</strong>
</article>
)
}
function LabUsageSection(props: { lab: ModelCatalogLab; data: StatsLabData | null }) {
const i18n = useI18n()
const activeLineClipId = createUniqueId()
const activeLineMaskId = createUniqueId()
const [activeIndex, setActiveIndex] = createSignal<number>()
const usage = createMemo(() => props.data?.usage ?? [])
const max = createMemo(() => Math.max(0, ...usage().map((item) => item.tokens)) || 1)
const tokenMax = createMemo(() => Math.max(0, ...usage().map((item) => item.tokens)) || 1)
const userMax = createMemo(() => Math.max(0, ...usage().map((item) => item.users)) || 1)
const linePoints = createMemo(() =>
usage().map((point, index) => ({
point,
x: usagePointX(index, usage().length),
y: usageLineY(point.users, userMax()),
})),
)
const userLinePath = createMemo(() => usageLinePath(linePoints()))
const activeLineBreak = createMemo(() => {
const index = activeIndex()
if (index === undefined) return undefined
const points = linePoints()
if (points.length < 2) return undefined
return usageColumnBounds(index, points.length)
})
const activeLineClip = createMemo(() => {
const index = activeIndex()
if (index === undefined) return undefined
const points = linePoints()
if (points.length < 2) return undefined
return usageColumnInnerBounds(index, points.length)
})
const monthTicks = createMemo(() => labUsageMonthTicks(usage()))
const activePoint = createMemo(() => {
const index = activeIndex()
if (index === undefined) return undefined
return usage()[index]
})
const activeTooltip = createMemo(() => {
const index = activeIndex()
const point = activePoint()
if (index === undefined || !point) return undefined
const bounds = usageColumnBounds(index, usage().length)
return {
bounds,
index,
point,
userY: linePoints()[index]?.y ?? 100,
}
})
return (
<section id="usage" data-section="model-panel">
<SectionHeading
href="#usage"
title={i18n.t("lab.usageTitle", { lab: props.lab.name })}
description={i18n.t("lab.usageDescription")}
/>
<SectionHeading href="#usage" title={i18n.t("nav.usage")} description={i18n.t("lab.usageDescription")} />
<Show
when={usage().some((item) => item.tokens > 0)}
fallback={<LabEmptyState title={i18n.t("lab.noUsageTitle")} description={i18n.t("lab.noUsageDescription")} />}
>
<div
data-component="model-usage-chart"
data-variant="lab-usage"
data-dense-labels={isLabUsageDense(usage().length) ? "true" : undefined}
role="img"
aria-label={i18n.t("lab.dailyTokenChart", { lab: props.lab.name })}
aria-label={`${props.lab.name} daily token volume and unique active user chart`}
style={{ "--model-usage-count": usage().length } as JSX.CSSProperties}
onPointerLeave={(event) => {
if (event.pointerType === "touch") return
setActiveIndex(undefined)
}}
>
<div data-slot="model-usage-axis" aria-hidden="true">
<For each={usage()}>
{(point, index) => (
<div
data-active={activeIndex() === index() ? "true" : undefined}
data-label-hidden={isLabUsageLabelHidden(index(), usage().length) ? "true" : undefined}
>
<span data-slot="model-usage-label">
<span data-slot="model-usage-total">{formatTokens(point.tokens)}</span>
<span data-slot="model-usage-date">{point.date}</span>
</span>
</div>
)}
</For>
</div>
<div data-slot="model-usage-bars">
<For each={usage()}>
{(point, index) => (
<div
data-slot="model-usage-column"
role="button"
tabIndex={0}
aria-label={`${point.date} ${formatTokens(point.tokens)} ${i18n.t("lab.tokens")}`}
data-active={activeIndex() === index() ? "true" : undefined}
data-muted={activeIndex() !== undefined && activeIndex() !== index() ? "true" : undefined}
onPointerDown={(event) => {
if (event.pointerType !== "touch") return
setActiveIndex(index())
}}
onPointerEnter={() => setActiveIndex(index())}
onPointerMove={(event) => {
if (event.pointerType === "touch") return
setActiveIndex(index())
}}
onClick={() => setActiveIndex(index())}
onFocus={() => setActiveIndex(index())}
onBlur={() => setActiveIndex(undefined)}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return
event.preventDefault()
setActiveIndex(index())
}}
>
<div
data-slot="model-usage-bar"
style={{ "--model-usage-fill": `${usageHeight(point.tokens, max())}%` } as JSX.CSSProperties}
/>
<Show when={activeIndex() === index() && activePoint()}>
{(active) => (
<div
data-component="chart-tooltip"
data-placement={index() > usage().length * 0.62 ? "left" : "right"}
<div data-slot="lab-usage-plot">
<Show when={userLinePath()}>
{(path) => (
<>
<svg
data-slot="lab-usage-line"
data-layer="base"
viewBox="0 0 100 100"
preserveAspectRatio="none"
aria-hidden="true"
>
<Show when={activeLineBreak()} fallback={<path data-slot="lab-usage-line-base" d={path()} />}>
{(lineBreak) => (
<>
<defs>
<mask id={activeLineMaskId} maskUnits="userSpaceOnUse">
<rect x="0" y="-2" width="100" height="104" fill="white" />
<rect x={lineBreak().x} y="-2" width={lineBreak().width} height="104" fill="black" />
</mask>
</defs>
<path data-slot="lab-usage-line-base" d={path()} mask={`url(#${activeLineMaskId})`} />
</>
)}
</Show>
</svg>
<Show when={activeLineClip()}>
{(clip) => (
<svg
data-slot="lab-usage-line"
data-layer="active"
viewBox="0 0 100 100"
preserveAspectRatio="none"
aria-hidden="true"
>
<strong>{active().date}</strong>
<span>
{formatTokens(active().tokens)} {i18n.t("lab.tokens")}
</span>
<div data-slot="tooltip-divider" />
<p>
<span data-slot="tooltip-label">
<i /> {i18n.t("lab.dailyTokens")}
</span>
<b>{formatTokens(active().tokens)}</b>
</p>
</div>
<defs>
<clipPath id={activeLineClipId} clipPathUnits="userSpaceOnUse">
<rect x={clip().x} y="-2" width={clip().width} height="104" />
</clipPath>
</defs>
<path data-slot="lab-usage-line-active" d={path()} clip-path={`url(#${activeLineClipId})`} />
</svg>
)}
</Show>
</div>
</>
)}
</Show>
<div data-slot="lab-usage-bars">
<For each={usage()}>
{(point, index) => (
<div
data-slot="lab-usage-column"
role="button"
tabIndex={0}
aria-label={`${point.date} ${formatTokens(point.tokens)} ${i18n.t("lab.tokens")}, ${formatUsers(point.users)} ${i18n.t("format.users")}`}
data-active={activeIndex() === index() ? "true" : undefined}
data-muted={activeIndex() !== undefined && activeIndex() !== index() ? "true" : undefined}
style={
{
"--lab-usage-token-height": `${usageStripHeight(point.tokens, tokenMax())}px`,
"--lab-usage-user-y": `${linePoints()[index()]?.y ?? 100}`,
} as JSX.CSSProperties
}
onPointerDown={(event) => {
if (event.pointerType !== "touch") return
setActiveIndex(index())
}}
onPointerEnter={() => setActiveIndex(index())}
onPointerMove={(event) => {
if (event.pointerType === "touch") return
setActiveIndex(index())
}}
onClick={() => setActiveIndex(index())}
onFocus={() => setActiveIndex(index())}
onBlur={() => setActiveIndex(undefined)}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return
event.preventDefault()
setActiveIndex(index())
}}
>
<div data-slot="lab-usage-token-bar" />
</div>
)}
</For>
</div>
<Show when={activeTooltip()} keyed>
{(active) => {
return (
<div
data-component="chart-tooltip"
data-placement={active.index > usage().length * 0.62 ? "left" : "right"}
style={
{
"--lab-usage-tooltip-left": `${active.bounds.x}%`,
"--lab-usage-tooltip-right": `${active.bounds.x + active.bounds.width}%`,
"--lab-usage-user-y": `${active.userY}`,
} as JSX.CSSProperties
}
>
<strong>{formatUsageTooltipDate(active.point.date)}</strong>
<span>
{formatTokens(active.point.tokens)} {i18n.t("lab.tokens")}
</span>
<div data-slot="tooltip-divider" />
<p>
<span data-slot="tooltip-label">
<i data-kind="tokens" /> {i18n.t("lab.dailyTokens")}
</span>
<b>{formatTokens(active.point.tokens)}</b>
</p>
<p>
<span data-slot="tooltip-label">
<i data-kind="users" /> {i18n.t("model.uniqueUsers")}
</span>
<b>{formatUsers(active.point.users)}</b>
</p>
</div>
)
}}
</Show>
</div>
<div data-slot="lab-usage-months" aria-hidden="true">
<For each={monthTicks()}>
{(tick) => (
<span
data-align={tick.align}
style={{ "--lab-usage-month-left": `${tick.left}%` } as JSX.CSSProperties}
>
{tick.label}
</span>
)}
</For>
</div>
@@ -347,50 +500,224 @@ function LabUsageSection(props: { lab: ModelCatalogLab; data: StatsLabData | nul
function LabModelsSection(props: { lab: ModelCatalogLab; usage: LabUsageModelEntry[] }) {
const i18n = useI18n()
const [activeTooltip, setActiveTooltip] = createSignal<LabModelTooltipState>()
const usageBySlug = createMemo(() => new Map(props.usage.map((item) => [item.slug, item])))
return (
<section id="models" data-section="model-panel">
<SectionHeading
href="#models"
title={i18n.t("lab.modelsTitle", { lab: props.lab.name })}
description={i18n.t("lab.recentUsageAndLimits")}
/>
<div data-component="lab-model-grid">
<For each={props.lab.models}>
{(model) => <LabModelCard model={model} usage={usageBySlug().get(model.slug)} />}
</For>
<section id="models" data-section="model-panel" data-variant="lab-models">
<div data-slot="lab-model-heading">
<SectionHeading href="#models" title={i18n.t("nav.models")} description={i18n.t("lab.recentUsageAndLimits")} />
<button data-slot="lab-model-compare" type="button" aria-label="Compare models" hidden>
<span data-slot="lab-model-compare-icon" aria-hidden="true" />
<span>Compare</span>
</button>
</div>
<div data-slot="lab-model-pattern" aria-hidden="true" />
<div
data-component="lab-model-table"
role="table"
aria-label={i18n.t("lab.modelsTitle", { lab: props.lab.name })}
>
<div data-slot="lab-model-table-track">
<div data-slot="lab-model-table-head" role="row">
<span data-column="model" role="columnheader">
{i18n.t("nav.models")}
</span>
<span data-column="usage" role="columnheader">
{i18n.t("lab.usage")}
</span>
<span data-column="share" role="columnheader">
{i18n.t("lab.share")}
</span>
<span data-column="context" role="columnheader">
{i18n.t("model.context")}
</span>
<span data-column="output" role="columnheader">
{i18n.t("model.output")}
</span>
<span data-column="release" role="columnheader">
{i18n.t("model.release")}
</span>
</div>
<For each={props.lab.models}>
{(model) => (
<LabModelRow model={model} usage={usageBySlug().get(model.slug)} onTooltipChange={setActiveTooltip} />
)}
</For>
</div>
</div>
<Show when={activeTooltip()} keyed>
{(state) => <LabModelTooltip state={state} />}
</Show>
</section>
)
}
function LabModelRow(props: {
model: ModelCatalogEntry
onTooltipChange: (state: LabModelTooltipState | undefined) => void
usage: LabUsageModelEntry | undefined
}) {
const i18n = useI18n()
const language = useLanguage()
const showTooltip = (target: HTMLAnchorElement) => {
const rect = target.getBoundingClientRect()
const viewportWidth = typeof window === "undefined" ? 0 : window.innerWidth
const viewportHeight = typeof window === "undefined" ? 0 : window.innerHeight
const anchorX = viewportWidth > 0 ? Math.min(Math.max(rect.left + 320, 24), viewportWidth - 24) : rect.left + 320
props.onTooltipChange({
model: props.model,
placement: viewportWidth > 0 && anchorX > viewportWidth - 280 ? "left" : "right",
usage: props.usage,
x: anchorX,
y:
viewportHeight > 0
? Math.min(Math.max(rect.top + rect.height / 2, 96), viewportHeight - 128)
: rect.top + rect.height / 2,
})
}
const showPointerTooltip: JSX.EventHandler<HTMLAnchorElement, PointerEvent> = (event) => {
if (event.pointerType === "touch") return
showTooltip(event.currentTarget)
}
const showFocusTooltip: JSX.EventHandler<HTMLAnchorElement, FocusEvent> = (event) => {
showTooltip(event.currentTarget)
}
return (
<a
data-component="lab-model-row"
href={language.route(`${import.meta.env.BASE_URL}${props.model.id}`)}
role="row"
aria-label={props.model.name}
onBlur={() => props.onTooltipChange(undefined)}
onFocus={showFocusTooltip}
onPointerEnter={showPointerTooltip}
onPointerDown={() => props.onTooltipChange(undefined)}
onPointerLeave={(event) => {
if (event.pointerType === "touch") return
props.onTooltipChange(undefined)
}}
onClick={() => props.onTooltipChange(undefined)}
>
<span data-slot="lab-model-cell" data-column="model" role="cell">
<span data-slot="lab-model-avatar" aria-hidden="true">
<ProviderIcon id={getProviderIconId(props.model.lab)} />
</span>
<strong>{props.model.name}</strong>
</span>
<span data-slot="lab-model-cell" data-column="usage" role="cell">
{props.usage ? formatTokens(props.usage.tokens) : "-"}
</span>
<span data-slot="lab-model-cell" data-column="share" role="cell">
{props.usage ? formatPercent(props.usage.share) : "-"}
</span>
<span data-slot="lab-model-cell" data-column="context" role="cell">
{formatCatalogLimit(props.model.limit?.context, i18n.t("home.unknown"))}
</span>
<span data-slot="lab-model-cell" data-column="output" role="cell">
{formatCatalogLimit(props.model.limit?.output, i18n.t("home.unknown"))}
</span>
<span data-slot="lab-model-cell" data-column="release" role="cell">
{formatCatalogDate(props.model.releaseDate, language.tag(language.locale()), i18n.t("home.unknown"))}
</span>
</a>
)
}
function LabModelTooltip(props: { state: LabModelTooltipState }) {
const i18n = useI18n()
return (
<div
data-component="lab-model-tooltip"
data-placement={props.state.placement}
style={
{
"--lab-model-tooltip-x": `${props.state.x}px`,
"--lab-model-tooltip-y": `${props.state.y}px`,
} as JSX.CSSProperties
}
>
<div data-slot="lab-model-tooltip-summary">
<div data-slot="lab-model-tooltip-head">
<span data-slot="lab-model-tooltip-avatar" aria-hidden="true">
<ProviderIcon id={getProviderIconId(props.state.model.lab)} />
</span>
<strong>{props.state.model.name}</strong>
</div>
<p>{props.state.model.description ?? "Recent OpenCode Go usage, share, context, and output limits."}</p>
</div>
<div data-slot="tooltip-divider" />
<div data-slot="lab-model-tooltip-metrics">
<p>
<span>{i18n.t("lab.usage")}</span>
<b>{props.state.usage ? formatTokens(props.state.usage.tokens) : "-"}</b>
</p>
<p>
<span>{i18n.t("lab.share")}</span>
<b>{props.state.usage ? formatPercent(props.state.usage.share) : "-"}</b>
</p>
<p>
<span>{i18n.t("model.context")}</span>
<b>{formatCatalogLimit(props.state.model.limit?.context, i18n.t("home.unknown"))}</b>
</p>
<p>
<span>{i18n.t("model.output")}</span>
<b>{formatCatalogLimit(props.state.model.limit?.output, i18n.t("home.unknown"))}</b>
</p>
</div>
</div>
)
}
function LabRelatedSection(props: { lab: ModelCatalogLab; labs: ModelCatalogLab[]; market: MarketDay[] }) {
const related = createMemo(() => relatedLabs(props.lab, props.labs, props.market))
return (
<section id="related-labs" data-section="model-panel" data-variant="lab-related">
<SectionHeading href="#related-labs" title="Related labs" description="Explore more." />
<div data-component="lab-related-list">
<For each={related()}>{(entry) => <LabRelatedCard entry={entry} />}</For>
</div>
</section>
)
}
function LabModelCard(props: { model: ModelCatalogEntry; usage: LabUsageModelEntry | undefined }) {
const i18n = useI18n()
function LabRelatedCard(props: { entry: RelatedLabEntry }) {
const language = useLanguage()
const featured = () => props.entry.lab.models[0]
const otherCount = () => Math.max(props.entry.lab.models.length - 1, 0)
const modelSummary = () => {
const model = featured()
if (!model) return props.entry.lab.name
const count = otherCount()
if (count === 0) return model.name
return `${model.name} + ${count} other ${count === 1 ? "model" : "models"}`
}
const activeBars = () => {
if (props.entry.share <= 0) return 0
return Math.max(1, Math.min(20, Math.round(props.entry.share / 5)))
}
return (
<a data-component="lab-model-card" href={language.route(`${import.meta.env.BASE_URL}${props.model.id}`)}>
<strong>{props.model.name}</strong>
<div data-slot="lab-model-card-meta">
<p>
<b>{i18n.t("lab.usage")}</b>
<em>{props.usage ? formatTokens(props.usage.tokens) : "—"}</em>
</p>
<p>
<b>{i18n.t("lab.share")}</b>
<em>{props.usage ? formatPercent(props.usage.share) : "—"}</em>
</p>
<p>
<b>{i18n.t("model.context")}</b>
<em>{formatCatalogLimit(props.model.limit?.context, i18n.t("home.unknown"))}</em>
</p>
<p>
<b>{i18n.t("model.output")}</b>
<em>{formatCatalogLimit(props.model.limit?.output, i18n.t("home.unknown"))}</em>
</p>
<p>
<b>{i18n.t("model.release")}</b>
<em>{formatCatalogDate(props.model.releaseDate, language.tag(language.locale()), i18n.t("home.unknown"))}</em>
</p>
<a
data-component="lab-related-card"
data-tone={relatedTone(props.entry.share)}
href={language.route(`${import.meta.env.BASE_URL}${props.entry.lab.id}`)}
>
<ProviderIcon data-slot="lab-related-watermark" aria-hidden="true" id={getProviderIconId(props.entry.lab.id)} />
<div data-slot="lab-related-heading">
<span data-slot="lab-related-avatar" aria-hidden="true">
<ProviderIcon id={getProviderIconId(props.entry.lab.id)} />
</span>
<strong>{props.entry.lab.name}</strong>
</div>
<p data-slot="lab-related-copy">{labRelatedDescription(props.entry.lab)}</p>
<p data-slot="lab-related-models">{modelSummary()}</p>
<div data-slot="lab-related-divider" />
<div data-slot="lab-related-usage">
<span>Used by {formatWholePercent(props.entry.share)}</span>
<i aria-hidden="true">
<For each={Array.from({ length: 20 })}>
{(_, index) => <b data-active={index() < activeBars() ? "true" : undefined} />}
</For>
</i>
</div>
</a>
)
@@ -405,6 +732,53 @@ function LabEmptyState(props: { title: string; description: string }) {
)
}
type RelatedLabEntry = { lab: ModelCatalogLab; share: number; tokens: number }
function relatedLabs(current: ModelCatalogLab, labs: ModelCatalogLab[], market: MarketDay[]): RelatedLabEntry[] {
const stats = relatedLabStats(labs, market)
return labs
.filter((lab) => lab.id !== current.id)
.map((lab) => stats.get(lab.id) ?? { lab, share: 0, tokens: 0 })
.toSorted((a, b) => b.tokens - a.tokens || a.lab.name.localeCompare(b.lab.name))
.slice(0, 3)
}
function relatedLabStats(labs: ModelCatalogLab[], market: MarketDay[]) {
const labByKey = new Map<string, ModelCatalogLab>()
labs.forEach((lab) => {
labByKey.set(lab.id, lab)
labByKey.set(catalogSlug(lab.name), lab)
labByKey.set(catalogSlug(formatCatalogLabName(lab.id)), lab)
})
const tokensByLab = new Map<string, number>()
const total = market.reduce((sum, day) => {
day.authors.forEach((author) => {
const lab = labByKey.get(catalogSlug(author.author))
if (!lab) return
tokensByLab.set(lab.id, (tokensByLab.get(lab.id) ?? 0) + author.tokens)
})
return sum + day.total
}, 0)
return new Map(
labs.map((lab) => {
const tokens = tokensByLab.get(lab.id) ?? 0
return [lab.id, { lab, tokens, share: total > 0 ? (tokens / total) * 100 : 0 }]
}),
)
}
function labRelatedDescription(lab: ModelCatalogLab) {
return lab.description ?? ""
}
function relatedTone(share: number) {
if (share >= 50) return "high"
if (share > 0 && share < 10) return "low"
return "mid"
}
function formatCatalogLimit(value: number | undefined, unknown: string) {
return value === undefined ? unknown : formatTokens(value)
}
@@ -433,6 +807,10 @@ function formatPercent(value: number) {
return `${trimNumber(value, value >= 10 ? 1 : 2)}%`
}
function formatWholePercent(value: number) {
return `${Math.round(value).toLocaleString("en")}%`
}
function formatTokens(value: number) {
if (value >= 1_000_000_000_000)
return `${trimNumber(value / 1_000_000_000_000, value >= 10_000_000_000_000 ? 0 : 1)}T`
@@ -442,21 +820,122 @@ function formatTokens(value: number) {
return String(Math.round(value))
}
function formatUsers(value: number) {
if (value >= 1_000_000) return `${trimNumber(value / 1_000_000, value >= 10_000_000 ? 0 : 1)}M`
if (value >= 1_000) return `${trimNumber(value / 1_000, value >= 10_000 ? 0 : 1)}K`
return new Intl.NumberFormat("en").format(Math.round(value))
}
function formatUsageTooltipDate(value: string) {
const match = /^([A-Z]{3})\s+(\d{1,2})$/.exec(value)
if (!match) return value
return `${monthName(match[1])} ${Number(match[2])} ${new Date().getFullYear()}`
}
function monthName(value: string) {
const names: Record<string, string> = {
JAN: "Jan",
FEB: "Feb",
MAR: "Mar",
APR: "Apr",
MAY: "May",
JUN: "Jun",
JUL: "Jul",
AUG: "Aug",
SEP: "Sep",
OCT: "Oct",
NOV: "Nov",
DEC: "Dec",
}
return names[value] ?? value
}
function trimNumber(value: number, digits: number) {
return Number(value.toFixed(digits)).toLocaleString("en")
}
function usageHeight(value: number, max: number) {
function usageStripHeight(value: number, max: number) {
if (value <= 0 || max <= 0) return 0
return Math.max(4, (value / max) * 100)
return Math.max(2, (value / max) * 76)
}
function usageLineY(value: number, max: number) {
if (value <= 0 || max <= 0) return 100
return Math.max(0, 100 - (value / max) * 100)
}
function usagePointX(index: number, count: number) {
if (count <= 1) return 50
return ((index + 0.5) / count) * 100
}
function usageColumnBounds(index: number, count: number) {
if (count <= 0) return { x: 0, width: 100 }
return { x: (index / count) * 100, width: 100 / count }
}
function usageColumnInnerBounds(index: number, count: number) {
const bounds = usageColumnBounds(index, count)
const inset = Math.min(bounds.width * 0.1, 0.24)
return { x: bounds.x + inset, width: Math.max(0.001, bounds.width - inset * 2) }
}
type UsageLinePoint = { x: number; y: number }
function usageLinePath(points: UsageLinePoint[]) {
if (points.length === 0) return ""
if (points.length === 1) return `M ${pathNumber(points[0].x)} ${pathNumber(points[0].y)}`
return points.slice(0, -1).reduce(
(path, point, index) => {
const next = points[index + 1]
const previous = points[index - 1] ?? point
const afterNext = points[index + 2] ?? next
const controlA = {
x: point.x + (next.x - previous.x) / 6,
y: clampUsagePercent(point.y + (next.y - previous.y) / 6),
}
const controlB = {
x: next.x - (afterNext.x - point.x) / 6,
y: clampUsagePercent(next.y - (afterNext.y - point.y) / 6),
}
return `${path} C ${pathNumber(controlA.x)} ${pathNumber(controlA.y)}, ${pathNumber(controlB.x)} ${pathNumber(controlB.y)}, ${pathNumber(next.x)} ${pathNumber(next.y)}`
},
`M ${pathNumber(points[0].x)} ${pathNumber(points[0].y)}`,
)
}
function pathNumber(value: number) {
return Number(value.toFixed(3))
}
function clampUsagePercent(value: number) {
return Math.max(0, Math.min(100, value))
}
function labUsageMonthTicks(points: ModelUsagePoint[]) {
const seen = new Set<string>()
return points.flatMap((point, index) => {
const label = point.date.split(" ")[0]
if (!label || seen.has(label)) return []
seen.add(label)
return [
{
label,
left: usagePointX(index, points.length),
align: index === 0 ? "start" : index >= points.length - 2 ? "end" : "center",
},
]
})
}
function isLabUsageDense(count: number) {
return count > 20
}
function isLabUsageLabelHidden(index: number, count: number) {
if (count <= 14) return false
const cadence = count > 45 ? 7 : count > 28 ? 4 : 2
return index % cadence !== 0 && index !== count - 1
function getProviderIconId(provider: string) {
const id = provider.toLowerCase().replace(/[^a-z0-9]+/g, "")
if (id === "moonshot") return "moonshotai"
if (id === "zhipu") return "zhipuai"
return id
}
File diff suppressed because it is too large Load Diff
+47 -21
View File
@@ -1262,30 +1262,56 @@ function MarketShareList(props: {
onActiveAuthorChange: (author: string) => void
}) {
const i18n = useI18n()
const language = useLanguage()
return (
<ol data-component="market-share-list">
<For each={props.data}>
{(item, index) => (
<li
role="button"
tabIndex={0}
aria-label={`${item.author} ${formatTrillions(item.tokens)} ${item.share.toFixed(1)} ${i18n.t("chart.percent")}`}
data-active={props.activeAuthor === item.author ? "true" : undefined}
onPointerEnter={() => props.onActiveAuthorChange(item.author)}
onFocus={() => props.onActiveAuthorChange(item.author)}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return
event.preventDefault()
props.onActiveAuthorChange(item.author)
}}
>
<span>{String(index() + 1).padStart(2, "0")}</span>
<i style={{ background: getRankColor(item.author, index(), props.authorOrder, marketColors) }} />
<strong>{item.author}</strong>
<em>{formatTrillions(item.tokens)}</em>
<b>{item.share.toFixed(1)}%</b>
</li>
)}
{(item, index) => {
const label = () =>
`${item.author} ${formatTrillions(item.tokens)} ${item.share.toFixed(1)} ${i18n.t("chart.percent")}`
const content = () => (
<>
<span>{String(index() + 1).padStart(2, "0")}</span>
<i style={{ background: getRankColor(item.author, index(), props.authorOrder, marketColors) }} />
<strong>{item.author}</strong>
<em>{formatTrillions(item.tokens)}</em>
<b>{item.share.toFixed(1)}%</b>
</>
)
const href = () =>
item.author === "Other" ? undefined : language.route(`${import.meta.env.BASE_URL}${modelSlug(item.author)}`)
return (
<li
data-active={props.activeAuthor === item.author ? "true" : undefined}
onPointerEnter={() => props.onActiveAuthorChange(item.author)}
>
<Show
when={href()}
fallback={
<button
type="button"
aria-label={label()}
onClick={() => props.onActiveAuthorChange(item.author)}
onFocus={() => props.onActiveAuthorChange(item.author)}
>
{content()}
</button>
}
>
{(href) => (
<a
href={href()}
aria-label={label()}
onClick={() => props.onActiveAuthorChange(item.author)}
onFocus={() => props.onActiveAuthorChange(item.author)}
>
{content()}
</a>
)}
</Show>
</li>
)
}}
</For>
</ol>
)
+73 -5
View File
@@ -1,7 +1,8 @@
import { query } from "@solidjs/router"
export const modelCatalogSourceUrl = "https://models.dev/models.json"
export const modelCatalogSourceUrl = "https://models.dev/catalog.json"
export const modelCatalogPricingUrl = "https://models.dev/api.json"
export const modelCatalogLabSourceUrl = "https://models.dev/labs"
export type ModelCatalogCost = {
input: number
@@ -15,6 +16,7 @@ export type ModelCatalogEntry = {
lab: string
slug: string
name: string
description?: string
family?: string
knowledge?: string
releaseDate?: string
@@ -45,6 +47,7 @@ export type ModelCatalogBenchmark = {
export type ModelCatalogLab = {
id: string
name: string
description?: string
models: ModelCatalogEntry[]
}
@@ -55,11 +58,12 @@ export type ModelCatalog = {
export const getModelCatalog = query(async () => {
"use server"
const [models, pricing] = await Promise.all([
const [models, pricing, labs] = await Promise.all([
fetchCatalogPayload(modelCatalogSourceUrl),
fetchCatalogPayload(modelCatalogPricingUrl),
fetchLabCatalogPayload(modelCatalogLabSourceUrl),
])
return buildModelCatalog(models, pricing)
return buildModelCatalog(models, pricing, labs)
}, "getModelCatalog")
export function findModelCatalogEntry(catalog: ModelCatalog, model: string, lab?: string) {
@@ -111,9 +115,10 @@ export function catalogSlug(value: string) {
.replace(/-{2,}/g, "-")
}
function buildModelCatalog(payload: unknown, pricingPayload?: unknown): ModelCatalog {
function buildModelCatalog(payload: unknown, pricingPayload?: unknown, labPayload?: unknown): ModelCatalog {
const costs = readCatalogCosts(pricingPayload)
const models = (Array.isArray(payload) ? payload : isRecord(payload) ? Object.values(payload) : [])
const labDescriptions = readCatalogLabDescriptions(payload, pricingPayload, labPayload)
const models = readCatalogModels(payload)
.flatMap(readModelCatalogEntry)
.map((model) => ({
...model,
@@ -131,6 +136,7 @@ function buildModelCatalog(payload: unknown, pricingPayload?: unknown): ModelCat
result[model.lab] = {
id: model.lab,
name: formatCatalogLabName(model.lab),
description: result[model.lab]?.description ?? labDescriptions.get(model.lab),
models: [...(result[model.lab]?.models ?? []), model],
}
return result
@@ -152,6 +158,7 @@ function readModelCatalogEntry(value: unknown): ModelCatalogEntry[] {
lab: catalogSlug(lab),
slug: catalogSlug(slug),
name,
description: stringValue(value.description),
family: stringValue(value.family),
knowledge: stringValue(value.knowledge),
releaseDate: stringValue(value.release_date),
@@ -170,12 +177,73 @@ function readModelCatalogEntry(value: unknown): ModelCatalogEntry[] {
]
}
function readCatalogModels(payload: unknown): unknown[] {
if (Array.isArray(payload)) return payload
if (!isRecord(payload)) return []
if (Array.isArray(payload.models)) return payload.models
if (isRecord(payload.models)) return Object.values(payload.models)
return Object.values(payload)
}
function readCatalogLabDescriptions(...payloads: unknown[]) {
const descriptions = new Map<string, string>()
const add = (value: unknown, fallbackId?: string) => {
if (!isRecord(value)) return
const description = stringValue(value.description)
if (!description) return
const id = stringValue(value.id) ?? fallbackId
const name = stringValue(value.name)
const title = stringValue(value.title)
const keys = [id, name, title]
keys.forEach((key) => {
if (!key) return
descriptions.set(catalogLabSlug(key), description)
})
}
const addCollection = (value: unknown) => {
if (Array.isArray(value)) {
value.forEach((item) => add(item))
return
}
if (!isRecord(value)) return
Object.entries(value).forEach(([key, item]) => add(item, key))
}
payloads.forEach((payload) => {
if (Array.isArray(payload)) {
payload.forEach((item) => add(item))
return
}
if (!isRecord(payload)) return
addCollection(payload.labs)
addCollection(payload.providers)
Object.entries(payload).forEach(([key, value]) => add(value, key))
})
return descriptions
}
async function fetchCatalogPayload(url: string) {
return fetch(url)
.then((response): Promise<unknown> => (response.ok ? (response.json() as Promise<unknown>) : Promise.resolve()))
.catch(() => undefined)
}
async function fetchLabCatalogPayload(url: string) {
return fetch(url)
.then((response) => (response.ok ? response.text() : Promise.resolve("")))
.then(readLabSearchIndex)
.catch(() => undefined)
}
function readLabSearchIndex(html: string) {
const match = /<script[^>]*id=["']search-index["'][^>]*>([\s\S]*?)<\/script>/.exec(html)
if (!match) return undefined
const parsed = JSON.parse(match[1]) as unknown
if (!Array.isArray(parsed)) return undefined
return parsed.filter((item) => isRecord(item) && item.type === "lab")
}
function readCatalogCosts(payload: unknown) {
const costs = new Map<string, ModelCatalogCost>()
const add = (model: unknown, provider?: string) => {
+3 -1
View File
@@ -1,10 +1,12 @@
import { LOCALE_HEADER } from "../lib/language"
const statsPageCacheControl = "public, max-age=60, s-maxage=300, stale-while-revalidate=86400"
export function setStatsPageCacheHeaders(headers: Headers | undefined) {
if (!headers) return
headers.set("Cache-Control", statsPageCacheControl)
appendVary(headers, "Accept-Language", "Cookie")
appendVary(headers, "Accept-Language", "Cookie", LOCALE_HEADER)
}
function appendVary(headers: Headers, ...values: string[]) {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/stats-core",
"version": "1.17.13",
"version": "1.17.14",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/stats-server",
"version": "1.17.13",
"version": "1.17.14",
"private": true,
"type": "module",
"license": "MIT",