feat: initial datalake and stats site (#28666)

This commit is contained in:
Adam
2026-05-25 17:34:04 -05:00
committed by GitHub
parent 633b5d6208
commit 5b02ac4d33
68 changed files with 8967 additions and 42 deletions

View File

@@ -0,0 +1,123 @@
:root {
color-scheme: light dark;
--stats-bg: #f8f5ee;
--stats-ink: #16110d;
--stats-muted: #6d6257;
--stats-line: #ded5c9;
--stats-panel: #fffaf1;
--stats-accent: #2357ff;
}
@media (prefers-color-scheme: dark) {
:root {
--stats-bg: #11100e;
--stats-ink: #f7efe4;
--stats-muted: #b8aa99;
--stats-line: #322d27;
--stats-panel: #1a1714;
--stats-accent: #86a2ff;
}
}
html {
line-height: 1;
background: var(--stats-bg);
}
body {
margin: 0;
min-width: 320px;
background:
radial-gradient(circle at top left, color-mix(in srgb, var(--stats-accent) 16%, transparent), transparent 32rem),
var(--stats-bg);
color: var(--stats-ink);
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
-webkit-font-smoothing: antialiased;
}
a {
color: inherit;
}
.shell {
box-sizing: border-box;
min-height: 100vh;
padding: 2rem clamp(1rem, 4vw, 4rem);
}
.panel {
display: grid;
gap: clamp(2rem, 8vw, 5rem);
box-sizing: border-box;
width: min(100%, 72rem);
margin: 0 auto;
padding: clamp(1.25rem, 4vw, 3rem);
border: 1px solid var(--stats-line);
border-radius: 1.5rem;
background: color-mix(in srgb, var(--stats-panel) 88%, transparent);
}
.eyebrow {
margin: 0 0 1rem;
color: var(--stats-muted);
font-size: 0.75rem;
letter-spacing: 0.14em;
text-transform: uppercase;
}
h1 {
max-width: 11ch;
margin: 0;
font-size: clamp(3rem, 14vw, 9rem);
line-height: 0.85;
letter-spacing: -0.08em;
}
.summary {
max-width: 42rem;
margin: 1.5rem 0 0;
color: var(--stats-muted);
font-size: clamp(1rem, 2vw, 1.25rem);
line-height: 1.6;
}
.grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 1px;
overflow: hidden;
border: 1px solid var(--stats-line);
border-radius: 1rem;
background: var(--stats-line);
}
.metric {
padding: 1rem;
background: var(--stats-panel);
}
.metric b {
display: block;
margin-bottom: 0.5rem;
font-size: clamp(1.5rem, 4vw, 3rem);
letter-spacing: -0.05em;
}
.metric span {
color: var(--stats-muted);
font-size: 0.8125rem;
}
.link {
display: inline-flex;
width: fit-content;
margin-top: 1.5rem;
color: var(--stats-accent);
text-decoration: none;
}
@media (max-width: 720px) {
.grid {
grid-template-columns: 1fr;
}
}

View File

@@ -0,0 +1,30 @@
import { MetaProvider, Meta, Title } from "@solidjs/meta"
import { Router } from "@solidjs/router"
import { FileRoutes } from "@solidjs/start/router"
import { Suspense } from "solid-js"
import "./app.css"
function AppMeta() {
return (
<>
<Title>opencode stats</Title>
<Meta name="description" content="OpenCode usage and stats." />
</>
)
}
export default function App() {
return (
<Router
explicitLinks={true}
root={(props) => (
<MetaProvider>
<AppMeta />
<Suspense>{props.children}</Suspense>
</MetaProvider>
)}
>
<FileRoutes />
</Router>
)
}

View File

@@ -0,0 +1,18 @@
<svg width="234" height="42" viewBox="0 0 234 42" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 30H6V18H18V30Z" fill="#4B4646"/>
<path d="M18 12H6V30H18V12ZM24 36H0V6H24V36Z" fill="#B7B1B1"/>
<path d="M48 30H36V18H48V30Z" fill="#4B4646"/>
<path d="M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z" fill="#B7B1B1"/>
<path d="M84 24V30H66V24H84Z" fill="#4B4646"/>
<path d="M84 24H66V30H84V36H60V6H84V24ZM66 18H78V12H66V18Z" fill="#B7B1B1"/>
<path d="M108 36H96V18H108V36Z" fill="#4B4646"/>
<path d="M108 12H96V36H90V6H108V12ZM114 36H108V12H114V36Z" fill="#B7B1B1"/>
<path d="M144 30H126V18H144V30Z" fill="#4B4646"/>
<path d="M144 12H126V30H144V36H120V6H144V12Z" fill="#F1ECEC"/>
<path d="M168 30H156V18H168V30Z" fill="#4B4646"/>
<path d="M168 12H156V30H168V12ZM174 36H150V6H174V36Z" fill="#F1ECEC"/>
<path d="M198 30H186V18H198V30Z" fill="#4B4646"/>
<path d="M198 12H186V30H198V12ZM204 36H180V6H198V0H204V36Z" fill="#F1ECEC"/>
<path d="M234 24V30H216V24H234Z" fill="#4B4646"/>
<path d="M216 12V18H228V12H216ZM234 24H216V30H234V36H210V6H234V24Z" fill="#F1ECEC"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,18 @@
<svg width="234" height="42" viewBox="0 0 234 42" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 30H6V18H18V30Z" fill="#CFCECD"/>
<path d="M18 12H6V30H18V12ZM24 36H0V6H24V36Z" fill="#656363"/>
<path d="M48 30H36V18H48V30Z" fill="#CFCECD"/>
<path d="M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z" fill="#656363"/>
<path d="M84 24V30H66V24H84Z" fill="#CFCECD"/>
<path d="M84 24H66V30H84V36H60V6H84V24ZM66 18H78V12H66V18Z" fill="#656363"/>
<path d="M108 36H96V18H108V36Z" fill="#CFCECD"/>
<path d="M108 12H96V36H90V6H108V12ZM114 36H108V12H114V36Z" fill="#656363"/>
<path d="M144 30H126V18H144V30Z" fill="#CFCECD"/>
<path d="M144 12H126V30H144V36H120V6H144V12Z" fill="#211E1E"/>
<path d="M168 30H156V18H168V30Z" fill="#CFCECD"/>
<path d="M168 12H156V30H168V12ZM174 36H150V6H174V36Z" fill="#211E1E"/>
<path d="M198 30H186V18H198V30Z" fill="#CFCECD"/>
<path d="M198 12H186V30H198V12ZM204 36H180V6H198V0H204V36Z" fill="#211E1E"/>
<path d="M234 24V30H216V24H234Z" fill="#CFCECD"/>
<path d="M216 12V18H228V12H216ZM234 24H216V30H234V36H210V6H234V24Z" fill="#211E1E"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,7 @@
// @refresh reload
import { mount, StartClient } from "@solidjs/start/client"
const root = document.getElementById("app")
if (!root) throw new Error("Root element #app not found")
mount(() => <StartClient />, root)

View File

@@ -0,0 +1,25 @@
// @refresh reload
import { createHandler, StartServer } from "@solidjs/start/server"
export default createHandler(
() => (
<StartServer
document={({ assets, children, scripts }) => (
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{assets}
</head>
<body>
<div id="app">{children}</div>
{scripts}
</body>
</html>
)}
/>
),
{
mode: "async",
},
)

1
packages/stats/app/src/global.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="@solidjs/start/env" />

View File

@@ -0,0 +1,19 @@
import { AppConfig } from "@opencode-ai/stats-core/config"
import { runtime } from "@opencode-ai/stats-core/runtime"
import { Effect } from "effect"
export async function GET() {
return Response.json(
await runtime.runPromise(
Effect.gen(function* () {
const config = yield* AppConfig
return {
ok: true,
app: "stats",
stage: config.stage,
publicUrl: config.publicUrl,
}
}),
),
)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,971 @@
import "./index.css"
import { Meta, Title } from "@solidjs/meta"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import {
type CountryEntry,
getStatsHomeData,
type LeaderboardEntry,
type MarketDay,
type StatsHomeData,
type SessionCostEntry,
type TokenCostEntry,
type UsagePoint,
} from "@opencode-ai/stats-core/domain/home"
import { runtime } from "@opencode-ai/stats-core/runtime"
import { createAsync, query } from "@solidjs/router"
import { scaleBand, scaleLinear } from "d3-scale"
import { createMemo, createSignal, For, Show, type JSX } from "solid-js"
import { getRequestEvent } from "solid-js/web"
import logoDark from "../asset/logo-ornate-dark.svg"
import logoLight from "../asset/logo-ornate-light.svg"
const products = ["All Users", "Zen", "Go", "Enterprise"] as const
const tokenProducts = ["Zen", "Go", "Enterprise"] as const
const ranges = ["1D", "1W", "1M", "3M", "YTD", "ALL"] as const
const usageColors = ["#ff5d64", "#ff8a00", "#8bef00", "#12c8b3", "#18c7dc", "#6c7dff", "#9d73f7"]
const marketColors = ["#ed6aff", "#a684ff", "#7c86ff", "#51a2ff", "#00d3f2", "#00d5be", "#00bc7d", "#9ae600", "#ffb900"]
const countryPositions = [
{ x: 112, y: 96 },
{ x: 284, y: 144 },
{ x: 472, y: 92 },
{ x: 642, y: 154 },
{ x: 800, y: 96 },
{ x: 172, y: 234 },
{ x: 362, y: 250 },
{ x: 552, y: 236 },
{ x: 744, y: 252 },
{ x: 48, y: 184 },
{ x: 892, y: 198 },
{ x: 456, y: 176 },
] as const
type UsageProduct = (typeof products)[number]
type TokenProduct = (typeof tokenProducts)[number]
type UsageRange = (typeof ranges)[number]
const getData = query(async () => {
"use server"
return runtime.runPromise(getStatsHomeData())
}, "getStatsHomeData")
export default function StatsHome() {
getRequestEvent()?.response.headers.set(
"Cache-Control",
"public, max-age=60, s-maxage=300, stale-while-revalidate=86400",
)
const data = createAsync(() => getData())
return (
<main data-page="stats">
<Title>OpenCode Stats</Title>
<Meta name="description" content="OpenCode usage, market share, token cost, and session cost stats." />
<div data-component="container">
<Header />
<div data-component="content">
<Show when={data()} fallback={<StatsLoading />}>
{(stats) => (
<>
<Hero updatedAt={stats().updatedAt} />
<UsageSection data={stats().usage} />
<LeaderboardSection data={stats().leaderboard} />
<MarketShareSection data={stats().market} />
<TokenCostSection data={stats().tokenCost} />
<SessionCostSection data={stats().sessionCost} />
<CountrySection data={stats().country} />
<Newsletter />
</>
)}
</Show>
</div>
<Footer />
</div>
<Legal />
</main>
)
}
function Hero(props: { updatedAt: string | null }) {
return (
<section data-section="hero">
<div>
<h1>OpenCode Stats</h1>
<p data-slot="meta">
<svg aria-hidden="true" width="16" height="16" viewBox="0 0 16 16">
<rect x="3" y="3" width="10" height="10" fill="currentColor" />
<rect x="7" y="6.5" width="2" height="4.5" fill="var(--stats-layer-2)" />
<rect x="7" y="5" width="2" height="1" fill="var(--stats-layer-2)" />
</svg>
<span>OpenCode data</span> <b>·</b>{" "}
<em>{props.updatedAt ? `Updated ${formatUpdatedAt(props.updatedAt)}` : "No rows yet"}</em>
</p>
</div>
<p>See how model usage, provider share, cost, and geography move across OpenCode traffic.</p>
</section>
)
}
function StatsLoading() {
return (
<>
<Hero updatedAt={null} />
<ChartSection title="Usage">
<EmptyState title="Loading stats" description="Reading model aggregates from model_stat." />
</ChartSection>
</>
)
}
function ChartSection(props: { title: string; description?: string; controls?: JSX.Element; children: JSX.Element }) {
return (
<section data-section="chart">
<div data-slot="section-header">
<div>
<h2>{props.title}</h2>
{props.description && <p>{props.description}</p>}
</div>
{props.controls}
</div>
{props.children}
</section>
)
}
function EmptyState(props: { title: string; description: string }) {
return (
<div data-component="empty-state">
<strong>{props.title}</strong>
<p>{props.description}</p>
</div>
)
}
function formatUpdatedAt(value: string) {
const date = new Date(value)
if (Number.isNaN(date.getTime())) return "just now"
return new Intl.DateTimeFormat("en", {
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
timeZone: "UTC",
timeZoneName: "short",
}).format(date)
}
function UsageSection(props: { data: StatsHomeData["usage"] }) {
const [product, setProduct] = createSignal<UsageProduct>("All Users")
const [range, setRange] = createSignal<UsageRange>("1W")
const data = createMemo(() => props.data[product()][range()])
return (
<ChartSection title="Usage">
<Show
when={data().some((item) => usageTotal(item) > 0)}
fallback={<EmptyState title="No usage data" description="No model_stat rows matched this product and range." />}
>
<UsageChart data={data()} />
</Show>
<div data-slot="chart-footer">
<StatsFilters product={product()} range={range()} onProductSelect={setProduct} onRangeSelect={setRange} />
</div>
</ChartSection>
)
}
function StatsFilters(props: {
product: UsageProduct
range: UsageRange
onProductSelect: (product: UsageProduct) => void
onRangeSelect: (range: UsageRange) => void
}) {
return (
<>
<FilterPills
items={products}
selected={props.product}
label="Product filter"
variant="product"
onSelect={props.onProductSelect}
/>
<FilterPills
items={ranges}
selected={props.range}
label="Date range"
variant="range"
onSelect={props.onRangeSelect}
/>
</>
)
}
function FilterPills<T extends string>(props: {
items: readonly T[]
selected: T
label: string
variant: "product" | "range"
onSelect: (item: T) => void
}) {
return (
<div data-component="usage-filter" data-variant={props.variant} role="radiogroup" aria-label={props.label}>
<For each={props.items}>
{(item) => (
<button
type="button"
role="radio"
aria-checked={props.selected === item}
data-active={props.selected === item ? "true" : undefined}
onClick={() => props.onSelect(item)}
>
{item}
</button>
)}
</For>
</div>
)
}
function UsageChart(props: { data: UsagePoint[] }) {
const [activeIndex, setActiveIndex] = createSignal<number>()
const [activeSegment, setActiveSegment] = createSignal<number>()
const height = 434
const width = 920
const headerOffset = 46
const segmentGap = 2
const maxTotal = createMemo(() => Math.max(1, Math.max(...props.data.map((item) => usageTotal(item))) * 1.02))
const activePoint = createMemo(() => props.data[activeIndex() ?? -1])
const y = createMemo(() => scaleLinear([0, maxTotal()], [height, 0]))
const x = createMemo(() =>
scaleBand(
props.data.map((_, index) => String(index)),
[0, width],
).paddingInner(0.08),
)
const activeBar = createMemo(() => {
const index = activeIndex()
const point = activePoint()
if (index === undefined) return
if (!point) return
return {
point,
x: x()(String(index)) ?? 0,
width: x().bandwidth(),
}
})
return (
<div data-component="usage-chart">
<svg viewBox={`0 0 ${width} ${height + headerOffset}`} role="img" aria-label="Stacked usage chart">
<defs>
<pattern id="stats-usage-dot-grid" width="6" height="6" patternUnits="userSpaceOnUse">
<rect x="1" y="1" width="2" height="2" fill="var(--stats-dot)" />
</pattern>
</defs>
<For each={props.data}>
{(day, dayIndex) => {
const barX = x()(String(dayIndex())) ?? 0
const barWidth = x().bandwidth()
const stackTop = y()(usageTotal(day))
return (
<g
role="button"
tabIndex={0}
aria-label={`${day.date} ${formatTokens(usageTotal(day))}`}
data-active={activeIndex() === dayIndex() ? "true" : undefined}
onPointerEnter={() => {
setActiveIndex(dayIndex())
setActiveSegment(undefined)
}}
onPointerLeave={(event) => {
if (event.pointerType === "touch") return
setActiveIndex(undefined)
setActiveSegment(undefined)
}}
onClick={() => setActiveIndex(dayIndex())}
onFocus={() => {
setActiveIndex(dayIndex())
setActiveSegment(undefined)
}}
onBlur={() => {
setActiveIndex(undefined)
setActiveSegment(undefined)
}}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return
event.preventDefault()
setActiveIndex(dayIndex())
}}
>
<rect
x={barX}
y="0"
width={barWidth}
height={height + headerOffset}
fill="transparent"
pointer-events="all"
/>
<text x={barX} y="17" class="chart-total">
{formatTokens(usageTotal(day))}
</text>
<text x={barX} y="34" class="chart-date">
{day.date}
</text>
<rect x={barX} y={headerOffset} width={barWidth} height={stackTop} fill="url(#stats-usage-dot-grid)" />
<For each={day.segments}>
{(segment, index) => {
const previous = day.segments.slice(0, index()).reduce((sum, item) => sum + item.value, 0)
const segmentHeight = y()(previous) - y()(previous + segment.value)
const segmentInset = index() === day.segments.length - 1 ? 0 : segmentGap
return (
<rect
x={barX}
y={headerOffset + y()(previous + segment.value) + segmentInset}
width={barWidth}
height={Math.max(segmentHeight - segmentInset, 0)}
data-segment-active={
activeIndex() === dayIndex() && activeSegment() === index() ? "true" : undefined
}
opacity={getUsageSegmentOpacity(activeIndex() === dayIndex(), activeSegment(), index())}
fill={activeIndex() === dayIndex() ? usageColors[index()] : "var(--stats-bar-idle)"}
onPointerEnter={(event) => {
event.stopPropagation()
setActiveIndex(dayIndex())
setActiveSegment(index())
}}
/>
)
}}
</For>
</g>
)
}}
</For>
</svg>
<Show when={activeBar()}>
{(bar) => (
<div
data-component="chart-tooltip"
data-placement={bar().x > width * 0.62 ? "left" : "right"}
style={getUsageTooltipStyle(bar().x, bar().width, width)}
>
<strong>{bar().point.date}</strong>
<span>{formatTokens(usageTotal(bar().point))} total</span>
<div data-slot="tooltip-divider" />
<For each={bar().point.segments}>
{(segment, index) => (
<p data-active={activeSegment() === index() ? "true" : undefined}>
<span data-slot="tooltip-label">
<i style={{ background: usageColors[index()] }} /> {segment.model}
</span>
<b>{formatTokens(segment.value)}</b>
</p>
)}
</For>
</div>
)}
</Show>
</div>
)
}
function getUsageTooltipStyle(barX: number, barWidth: number, width: number) {
if (barX > width * 0.62) return { left: "auto", right: `${((width - barX + 12) / width) * 100}%` }
return { left: `${((barX + barWidth + 12) / width) * 100}%`, right: "auto" }
}
function getUsageSegmentOpacity(isActiveBar: boolean, activeSegment: number | undefined, index: number) {
if (!isActiveBar) return 1
if (activeSegment === undefined) return 1
return activeSegment === index ? 1 : 0.38
}
function usageTotal(point: UsagePoint) {
return point.segments.reduce((sum, item) => sum + item.value, 0)
}
function formatTokens(value: number) {
if (value >= 1) return `${value.toFixed(value >= 10 ? 0 : 1)}T`
return `${Math.round(value * 1000)}B`
}
function LeaderboardSection(props: { data: StatsHomeData["leaderboard"] }) {
const [product, setProduct] = createSignal<UsageProduct>("All Users")
const [range, setRange] = createSignal<UsageRange>("1W")
const data = createMemo(() => props.data[product()][range()])
return (
<ChartSection
title="Leaderboard"
description="Shown are the sum of prompt and completion tokens per model, including reasoning tokens."
>
<Show
when={data().length > 0}
fallback={
<EmptyState title="No leaderboard data" description="No model_stat rows matched this product and range." />
}
>
<Leaderboard data={data()} />
</Show>
<div data-slot="chart-footer">
<StatsFilters product={product()} range={range()} onProductSelect={setProduct} onRangeSelect={setRange} />
</div>
</ChartSection>
)
}
function Leaderboard(props: { data: LeaderboardEntry[] }) {
return (
<div data-component="leaderboard" aria-label="Model token leaderboard">
<div data-slot="leaderboard-grid">
<div data-slot="leaderboard-featured">
<For each={props.data.slice(0, 3)}>{(entry) => <LeaderboardCard entry={entry} size="featured" />}</For>
</div>
<div data-slot="leaderboard-compact">
<For each={props.data.slice(3)}>{(entry) => <LeaderboardCard entry={entry} size="compact" />}</For>
</div>
</div>
</div>
)
}
function LeaderboardCard(props: { entry: LeaderboardEntry; size: "featured" | "compact" }) {
return (
<article data-component="leader-card" data-size={props.size}>
<span data-slot="rank">{String(props.entry.rank).padStart(2, "0")}</span>
<ProviderIcon data-slot="leader-watermark" aria-hidden="true" id={getProviderIconId(props.entry.author)} />
<div data-slot="leader-body">
<ProviderIcon data-slot="leader-avatar" aria-hidden="true" id={getProviderIconId(props.entry.author)} />
<div data-slot="leader-copy">
<div>
<strong>{props.entry.model}</strong>
<span>{formatBillions(props.entry.tokens)}</span>
</div>
<div>
<span>{props.entry.author}</span>
<span data-slot="delta" data-negative={props.entry.change < 0 ? "true" : undefined}>
{formatChange(props.entry.change)}
</span>
</div>
</div>
</div>
</article>
)
}
function getProviderIconId(author: string) {
if (author === "MiniMax") return "minimax"
if (author === "Moonshot") return "moonshotai"
if (author === "Zhipu") return "zhipuai"
return author.toLowerCase()
}
function formatBillions(value: number) {
if (value >= 1000) return `${(value / 1000).toFixed(value >= 10000 ? 0 : 1)}T`
return `${value}B`
}
function formatChange(value: number) {
if (value > 0) return `+${value}%`
return `${value}%`
}
function MarketShareSection(props: { data: StatsHomeData["market"] }) {
const [range, setRange] = createSignal<UsageRange>("1W")
const [activeIndex, setActiveIndex] = createSignal(2)
const data = createMemo(() => props.data[range()])
const selectedIndex = createMemo(() => Math.min(activeIndex(), Math.max(data().length - 1, 0)))
const activeDay = createMemo(() => data()[selectedIndex()])
return (
<ChartSection title="Market Share" description="Compare token share by model author.">
<Show
when={activeDay()}
fallback={<EmptyState title="No market data" description="No model_stat rows matched this range." />}
>
{(day) => (
<>
<MarketShare data={data()} activeIndex={selectedIndex()} onActiveIndexChange={setActiveIndex} />
<MarketShareList data={day().authors} />
</>
)}
</Show>
<div data-slot="market-footer">
<p>
<span>[*]</span>
<strong>{activeDay()?.date ?? "No data"}</strong>
</p>
<FilterPills items={ranges} selected={range()} label="Date range" variant="range" onSelect={setRange} />
</div>
</ChartSection>
)
}
function MarketShare(props: { data: MarketDay[]; activeIndex: number; onActiveIndexChange: (index: number) => void }) {
return (
<div data-component="market-share" role="img" aria-label="Market share by model author">
<div data-slot="market-labels">
<For each={props.data}>
{(day, index) => (
<button
type="button"
data-active={props.activeIndex === index() ? "true" : undefined}
onClick={() => props.onActiveIndexChange(index())}
>
<span>{formatTrillions(day.total)}</span>
<span>{day.date}</span>
</button>
)}
</For>
</div>
<div data-slot="market-bars">
<For each={props.data}>
{(day, index) => (
<button
type="button"
aria-label={`${day.date} ${formatTrillions(day.total)}`}
data-active={props.activeIndex === index() ? "true" : undefined}
onClick={() => props.onActiveIndexChange(index())}
>
<For each={day.authors}>
{(author, authorIndex) => (
<span
style={{
"background-color": props.activeIndex === index() ? marketColors[authorIndex()] : undefined,
"flex-grow": author.share,
}}
/>
)}
</For>
</button>
)}
</For>
</div>
</div>
)
}
function MarketShareList(props: { data: MarketDay["authors"] }) {
return (
<ol data-component="market-share-list">
<For each={props.data}>
{(item, index) => (
<li>
<span>{String(index() + 1).padStart(2, "0")}</span>
<i style={{ background: marketColors[index()] }} />
<strong>{item.author}</strong>
<em>{formatTrillions(item.tokens)}</em>
<b>{item.share.toFixed(1)}%</b>
</li>
)}
</For>
</ol>
)
}
function formatTrillions(value: number) {
return `${value.toFixed(value >= 10 ? 0 : 1)}T`
}
function TokenCostSection(props: { data: StatsHomeData["tokenCost"] }) {
const [product, setProduct] = createSignal<TokenProduct>("Zen")
const [activeIndex, setActiveIndex] = createSignal(2)
const data = createMemo(() => props.data[product()])
const selectedIndex = createMemo(() => Math.min(activeIndex(), Math.max(data().length - 1, 0)))
return (
<ChartSection title="Token Cost" description="Price per 1M tokens.">
<Show
when={data().length > 0}
fallback={
<EmptyState title="No token cost data" description="No cost-bearing model_stat rows matched this product." />
}
>
<TokenCostChart data={data()} activeIndex={selectedIndex()} onActiveIndexChange={setActiveIndex} />
</Show>
<div data-slot="token-footer">
<FilterPills
items={tokenProducts}
selected={product()}
label="Product filter"
variant="product"
onSelect={setProduct}
/>
</div>
</ChartSection>
)
}
function TokenCostChart(props: {
data: TokenCostEntry[]
activeIndex: number
onActiveIndexChange: (index: number) => void
}) {
const max = createMemo(() => Math.max(1, ...props.data.map((item) => item.total)))
const active = createMemo(() => props.data[props.activeIndex] ?? props.data[0])
return (
<div data-component="token-cost">
<For each={props.data}>
{(item, index) => (
<button
type="button"
data-component="token-row"
data-active={props.activeIndex === index() ? "true" : undefined}
onClick={() => props.onActiveIndexChange(index())}
onPointerEnter={() => props.onActiveIndexChange(index())}
>
<strong>{formatDollars(item.total)}</strong>
<span>{item.model}</span>
<MetricBar value={item.total} max={max()} active={props.activeIndex === index()} />
</button>
)}
</For>
<Show when={active()}>
{(item) => (
<div data-component="token-tooltip" style={{ top: `${props.activeIndex * 28 + 2}px` }}>
<p>
<span>Input</span>
<strong>{formatDollars(item().input)}</strong>
</p>
<p>
<span>Output</span>
<strong>{formatDollars(item().output)}</strong>
</p>
<p>
<span>Cached</span>
<strong>{formatDollars(item().cached)}</strong>
</p>
</div>
)}
</Show>
</div>
)
}
function formatDollars(value: number) {
return `$${value.toFixed(2)}`
}
function MetricBar(props: { value: number; max: number; active: boolean }) {
return (
<i data-component="metric-bar" data-active={props.active ? "true" : undefined}>
<b style={{ "flex-grow": Math.max(props.value / Math.max(props.max, 1), 0.05) }} />
<em />
</i>
)
}
function SessionCostSection(props: { data: StatsHomeData["sessionCost"] }) {
const [product, setProduct] = createSignal<TokenProduct>("Zen")
const [activeIndex, setActiveIndex] = createSignal(2)
const data = createMemo(() => props.data[product()])
const selectedIndex = createMemo(() => Math.min(activeIndex(), Math.max(data().length - 1, 0)))
return (
<ChartSection title="Session Cost" description="Average cost per session.">
<Show
when={data().length > 0}
fallback={
<EmptyState
title="No session cost data"
description="No session-bearing model_stat rows matched this product."
/>
}
>
<SessionCostChart data={data()} activeIndex={selectedIndex()} onActiveIndexChange={setActiveIndex} />
</Show>
<div data-slot="token-footer">
<FilterPills
items={tokenProducts}
selected={product()}
label="Product filter"
variant="product"
onSelect={setProduct}
/>
</div>
</ChartSection>
)
}
function SessionCostChart(props: {
data: SessionCostEntry[]
activeIndex: number
onActiveIndexChange: (index: number) => void
}) {
const maxCost = createMemo(() => Math.max(1, ...props.data.map((item) => item.cost)))
const maxTokens = createMemo(() => Math.max(1, ...props.data.map((item) => item.tokens)))
const active = createMemo(() => props.data[props.activeIndex] ?? props.data[0])
return (
<div data-component="session-cost">
<div data-slot="session-heading">
<span />
<p>COST / SESSION</p>
<p>TOKENS / SESSIONS</p>
</div>
<For each={props.data}>
{(item, index) => (
<button
type="button"
data-component="token-row"
data-variant="session"
data-active={props.activeIndex === index() ? "true" : undefined}
onClick={() => props.onActiveIndexChange(index())}
onPointerEnter={() => props.onActiveIndexChange(index())}
>
<strong>{formatSessionCost(item.cost)}</strong>
<span>{item.model}</span>
<MetricBar value={item.cost} max={maxCost()} active={props.activeIndex === index()} />
<MetricBar value={item.tokens} max={maxTokens()} active={props.activeIndex === index()} />
</button>
)}
</For>
<Show when={active()}>
{(item) => (
<div
data-component="token-tooltip"
data-variant="session"
style={{ top: `${props.activeIndex * 28 + 21}px` }}
>
<p>
<span>Cost/Session</span>
<strong>{formatSessionCost(item().cost)}</strong>
</p>
<p>
<span>Tokens/Session</span>
<strong>{formatTokenCount(item().tokens)}</strong>
</p>
</div>
)}
</Show>
</div>
)
}
function formatTokenCount(value: number) {
if (value >= 1_000_000) return `${Number((value / 1_000_000).toFixed(1))}M`
return `${Math.round(value / 1_000)}K`
}
function formatSessionCost(value: number) {
return `$${value.toFixed(4)}`
}
function CountrySection(props: { data: StatsHomeData["country"] }) {
const [range, setRange] = createSignal<UsageRange>("1W")
const data = createMemo(() => props.data[range()])
return (
<ChartSection title="Token by Country" description="Country-level token totals from geo_stat.">
<Show
when={data().length > 0}
fallback={<EmptyState title="No country data" description="No geo_stat rows matched this range." />}
>
<CountryChart data={data()} />
</Show>
<div data-slot="country-footer">
<p>
<span>[*]</span>
<strong>Top countries by tokens</strong>
</p>
<FilterPills items={ranges} selected={range()} label="Date range" variant="range" onSelect={setRange} />
</div>
</ChartSection>
)
}
function CountryChart(props: { data: CountryEntry[] }) {
const [activeIndex, setActiveIndex] = createSignal(0)
const selectedIndex = createMemo(() => Math.min(activeIndex(), Math.max(props.data.length - 1, 0)))
const active = createMemo(() => props.data[selectedIndex()])
const max = createMemo(() => Math.max(0.0001, ...props.data.map((item) => item.tokens)))
return (
<div data-component="country-map">
<svg viewBox="0 0 920 320" role="img" aria-label="Country token share bubble chart">
<For each={props.data.slice(0, countryPositions.length)}>
{(item, index) => {
const position = countryPositions[index()]
const radius = 18 + Math.sqrt(item.tokens / max()) * 58
return (
<g
role="button"
tabIndex={0}
aria-label={`${formatCountry(item.country)} ${formatTokens(item.tokens)}`}
data-active={selectedIndex() === index() ? "true" : undefined}
onPointerEnter={() => setActiveIndex(index())}
onClick={() => setActiveIndex(index())}
onFocus={() => setActiveIndex(index())}
>
<circle cx={position.x} cy={position.y} r={radius} />
<text x={position.x} y={position.y + 4} text-anchor="middle">
{item.country}
</text>
</g>
)
}}
</For>
</svg>
<Show when={active()}>
{(item) => (
<div data-component="map-tooltip">
<strong>{formatCountry(item().country)}</strong>
<span>{item().continent || "Unknown region"}</span>
<p>
<b>{formatTokens(item().tokens)}</b>
<em>{item().share.toFixed(1)}%</em>
</p>
</div>
)}
</Show>
<CountryList data={props.data.slice(0, 8)} activeIndex={selectedIndex()} onActiveIndexChange={setActiveIndex} />
</div>
)
}
function CountryList(props: {
data: CountryEntry[]
activeIndex: number
onActiveIndexChange: (index: number) => void
}) {
return (
<ol data-component="country-list">
<For each={props.data}>
{(item, index) => (
<li>
<button
type="button"
data-active={props.activeIndex === index() ? "true" : undefined}
onClick={() => props.onActiveIndexChange(index())}
onPointerEnter={() => props.onActiveIndexChange(index())}
>
<span>{String(item.rank).padStart(2, "0")}</span>
<strong>{formatCountry(item.country)}</strong>
<em>{formatTokens(item.tokens)}</em>
<b>{item.share.toFixed(1)}%</b>
</button>
</li>
)}
</For>
</ol>
)
}
function formatCountry(country: string) {
const known: Record<string, string> = {
AU: "Australia",
BR: "Brazil",
CA: "Canada",
CN: "China",
DE: "Germany",
FR: "France",
GB: "United Kingdom",
IN: "India",
JP: "Japan",
KR: "South Korea",
NL: "Netherlands",
SG: "Singapore",
US: "United States",
ZZ: "Unknown",
}
return known[country] ?? country
}
function Newsletter() {
return (
<section data-section="newsletter">
<div>
<h2>Be the first to know when we release new products</h2>
<p>Join the waitlist for early access.</p>
</div>
<form>
<input type="email" placeholder="Email address" />
<button>Subscribe</button>
</form>
</section>
)
}
function Header() {
return (
<section data-component="top">
<a data-slot="brand" href="https://opencode.ai/" aria-label="OpenCode home">
<img data-slot="logo light" src={logoLight} alt="OpenCode" width="234" height="42" />
<img data-slot="logo dark" src={logoDark} alt="OpenCode" width="234" height="42" />
</a>
<nav data-component="nav-desktop" aria-label="Main navigation">
<ul>
<li>
<a href="https://github.com/sst/opencode" target="_blank" rel="noreferrer">
GitHub
</a>
</li>
<li>
<a href="https://opencode.ai/docs">Docs</a>
</li>
<li>
<a href="https://opencode.ai/zen">Zen</a>
</li>
<li>
<a href="https://opencode.ai/go">Go</a>
</li>
<li>
<a href="https://opencode.ai/enterprise">Enterprise</a>
</li>
<li>
<a href="https://opencode.ai/download" data-slot="cta-button">
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true">
<path
d="M12.1875 9.75L9.00001 12.9375L5.8125 9.75M9.00001 2.0625L9 12.375M14.4375 15.9375H3.5625"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="square"
/>
</svg>
Download
</a>
</li>
</ul>
</nav>
</section>
)
}
function Footer() {
return (
<footer data-component="footer">
<div data-slot="cell">
<a href="https://github.com/sst/opencode" target="_blank" rel="noreferrer">
GitHub
</a>
</div>
<div data-slot="cell">
<a href="https://opencode.ai/docs">Docs</a>
</div>
<div data-slot="cell">
<a href="https://opencode.ai/changelog">Changelog</a>
</div>
<div data-slot="cell">
<a href="https://x.com/opencode_ai">X</a>
</div>
</footer>
)
}
function Legal() {
return (
<div data-component="legal">
<span>
©{new Date().getFullYear()} <a href="https://anoma.ly">Anomaly</a>
</span>
<span>
<a href="https://opencode.ai/brand">Brand</a>
</span>
<span>
<a href="https://opencode.ai/legal/privacy-policy">Privacy</a>
</span>
<span>
<a href="https://opencode.ai/legal/terms-of-service">Terms</a>
</span>
</div>
)
}