feat(stats): add unique user charts

This commit is contained in:
Adam
2026-06-20 14:54:45 -05:00
parent 2d993cd0d5
commit 24c70ec974
13 changed files with 327 additions and 127 deletions
+53 -5
View File
@@ -171,6 +171,7 @@ export default function StatsHome() {
<>
<Hero updatedAt={stats().updatedAt} />
<TopModelsSection data={stats().usage} leaderboard={stats().leaderboard} />
<UniqueUsersSection data={stats().users} />
<SessionCostSection data={stats().sessionCost} />
<TokenCostSection data={stats().tokenCost} catalog={catalog() ?? null} />
<CacheRatioSection data={stats().cacheRatio} />
@@ -598,6 +599,8 @@ function FilterPills<T extends string>(props: {
function TopModelsChart(props: {
data: UsagePoint[]
range: UsageRange
metric?: "tokens" | "users"
ariaLabel?: string
activeModel: string | undefined
onActiveModelChange: (model: string | undefined) => void
}) {
@@ -606,6 +609,7 @@ function TopModelsChart(props: {
const maxTotal = createMemo(() => getTopModelsMaxTotal(props.data))
const segmentOrder = createMemo(() => getTopModelsSegmentOrder(props.data))
const activePoint = createMemo(() => props.data[activeIndex() ?? -1])
const metric = createMemo(() => props.metric ?? "tokens")
createEffect(() => scrollDenseChartToEnd(chartRef, props.range, props.data.length))
@@ -614,9 +618,10 @@ function TopModelsChart(props: {
ref={chartRef}
data-component="top-models-chart"
data-range={props.range}
data-metric={metric()}
data-dense-labels={isDenseColumnRange(props.range) ? "true" : undefined}
role="img"
aria-label="Stacked top model usage chart"
aria-label={props.ariaLabel ?? "Stacked top model usage chart"}
style={{ "--top-models-count": props.data.length } as JSX.CSSProperties}
onPointerLeave={(event) => {
if (event.pointerType === "touch") return
@@ -633,7 +638,7 @@ function TopModelsChart(props: {
data-mobile-hidden={isTopModelsMobileAxisHidden(index(), props.data.length) ? "true" : undefined}
>
<span data-slot="axis-label">
<span data-slot="axis-total">{formatTokens(usageTotal(day))}</span>
<span data-slot="axis-total">{formatUsageChartValue(usageTotal(day), metric())}</span>
<span data-slot="axis-date">
<span data-slot="axis-date-full">{day.date}</span>
<span data-slot="axis-date-mobile">{formatTopModelsMobileDate(day.date, props.range)}</span>
@@ -657,7 +662,7 @@ function TopModelsChart(props: {
data-slot="top-models-bar"
role="button"
tabIndex={0}
aria-label={`${day.date} ${formatTokens(usageTotal(day))}`}
aria-label={`${day.date} ${formatUsageChartValue(usageTotal(day), metric())} ${usageChartTotalLabel(metric())}`}
data-active={activeIndex() === dayIndex() ? "true" : undefined}
data-muted={activeIndex() !== undefined && activeIndex() !== dayIndex() ? "true" : undefined}
style={{ "--top-models-bar-height": `${getTopModelsBarHeight(usageTotal(day), maxTotal())}%` }}
@@ -739,7 +744,9 @@ function TopModelsChart(props: {
data-placement={dayIndex() > props.data.length * 0.62 ? "left" : "right"}
>
<strong>{point().date}</strong>
<span>{formatTokens(usageTotal(point()))} total</span>
<span>
{formatUsageChartValue(usageTotal(point()), metric())} {usageChartTotalLabel(metric())}
</span>
<div data-slot="tooltip-divider" />
<For each={visibleTopModelsSegments(point())}>
{(item) => (
@@ -759,7 +766,7 @@ function TopModelsChart(props: {
/>{" "}
{item.segment.model}
</span>
<b>{formatTokens(item.segment.value)}</b>
<b>{formatUsageChartValue(item.segment.value, metric())}</b>
</p>
)}
</For>
@@ -774,6 +781,31 @@ function TopModelsChart(props: {
)
}
function UniqueUsersSection(props: { data: StatsHomeData["users"] }) {
const [activeModel, setActiveModel] = createSignal<string>()
const data = createMemo(() => props.data.Go["2M"])
return (
<section id="unique-users" data-section="unique-users">
<SectionBridge label="TOP MODELS" href="#top-models" />
<SectionTitle title="Unique Users" description="Daily unique OpenCode Go users by model." />
<Show
when={data().some((item) => usageTotal(item) > 0)}
fallback={<EmptyState title="No user data" description="No user-bearing model_stat rows matched this window." />}
>
<TopModelsChart
data={data()}
range="2M"
metric="users"
ariaLabel="Stacked unique user chart by model"
activeModel={activeModel()}
onActiveModelChange={setActiveModel}
/>
</Show>
</section>
)
}
function isTopModelsBlankHover(bar: HTMLElement, clientY: number) {
const stack = bar.querySelector<HTMLElement>('[data-slot="top-models-stack"]')
if (!stack) return true
@@ -864,6 +896,22 @@ function formatTokens(value: number) {
return `${Math.round(value * 1000)}B`
}
function formatUsageChartValue(value: number, metric: "tokens" | "users") {
if (metric === "users") return formatUsers(value)
return formatTokens(value)
}
function usageChartTotalLabel(metric: "tokens" | "users") {
if (metric === "users") return "model users"
return "total"
}
function formatUsers(value: number) {
if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(value >= 10_000_000 ? 0 : 1)}M`
if (value >= 1_000) return `${(value / 1_000).toFixed(value >= 10_000 ? 0 : 1)}K`
return new Intl.NumberFormat("en").format(Math.round(value))
}
function Leaderboard(props: {
data: LeaderboardEntry[]
activeModel: string | undefined