diff --git a/packages/stats/app/src/asset/logo-ornate-dark.svg b/packages/stats/app/src/asset/logo-ornate-dark.svg new file mode 100644 index 0000000000..a158273242 --- /dev/null +++ b/packages/stats/app/src/asset/logo-ornate-dark.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/stats/app/src/asset/logo-ornate-light.svg b/packages/stats/app/src/asset/logo-ornate-light.svg new file mode 100644 index 0000000000..2a856dccef --- /dev/null +++ b/packages/stats/app/src/asset/logo-ornate-light.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/stats/app/src/routes/index.css b/packages/stats/app/src/routes/index.css index 2b30c4b9bb..2e292346d8 100644 --- a/packages/stats/app/src/routes/index.css +++ b/packages/stats/app/src/routes/index.css @@ -21,6 +21,8 @@ --stats-accent-text: #6c7dff; --stats-bar-idle: #d4d4d4; --stats-dot: #d4d4d4; + --stats-page-padding: 5rem; + --stats-section-padding: 6rem; min-height: 100vh; display: flex; flex-direction: column; @@ -52,11 +54,10 @@ justify-content: space-between; height: 80px; min-height: 80px; - padding: 24px 80px; + padding: 24px var(--stats-page-padding); color: var(--stats-muted); - background: color-mix(in srgb, var(--stats-bg) 94%, transparent); + background: var(--stats-bg); border-bottom: 1px solid var(--stats-line); - backdrop-filter: blur(16px); font-family: "IBM Plex Mono", var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); @@ -74,9 +75,18 @@ text-underline-offset: 4px; } -[data-page="stats"] [data-slot="wordmark"] { - font-weight: 600; - letter-spacing: -0.04em; +[data-page="stats"] [data-slot="brand"] { + display: flex; + align-items: center; +} + +[data-page="stats"] [data-slot="brand"] img { + width: auto; + height: 34px; +} + +[data-page="stats"] [data-slot="logo dark"] { + display: none; } [data-page="stats"] [data-component="nav-desktop"] ul { @@ -88,6 +98,27 @@ list-style: none; } +[data-page="stats"] [data-component="nav-desktop"] a span { + color: var(--stats-faint); +} + +[data-page="stats"] [data-component="nav-desktop"] [data-slot="cta-button"] { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 16px 8px 10px; + border-radius: 4px; + background: var(--color-background-strong); + color: var(--color-text-inverted); + font-weight: 500; + white-space: nowrap; +} + +[data-page="stats"] [data-component="nav-desktop"] [data-slot="cta-button"]:hover { + background: var(--color-background-strong-hover); + text-decoration: none; +} + [data-page="stats"] [data-component="footer"] { display: flex; border-top: 1px solid var(--stats-line); @@ -157,7 +188,7 @@ [data-page="stats"] [data-section="chart"], [data-page="stats"] [data-section="newsletter"] { border-bottom: 1px solid var(--stats-line); - padding: 96px 80px; + padding: var(--stats-section-padding) var(--stats-page-padding); } [data-page="stats"] [data-section="hero"] { @@ -237,7 +268,7 @@ } [data-page="stats"] [data-section="chart"] { - min-height: 760px; + min-height: 0; } [data-page="stats"] [data-slot="section-header"] { @@ -943,21 +974,122 @@ [data-page="stats"] [data-component="country-map"] circle { fill: var(--stats-accent); - fill-opacity: 0.2; + fill-opacity: 0.14; stroke: var(--stats-accent); - stroke-width: 2; + stroke-width: 1.5; + transition: + fill-opacity 120ms ease, + stroke-width 120ms ease; +} + +[data-page="stats"] [data-component="country-map"] g[role="button"] { + cursor: pointer; + outline: none; +} + +[data-page="stats"] [data-component="country-map"] g[role="button"][data-active="true"] circle { + fill-opacity: 0.28; + stroke-width: 3; } [data-page="stats"] [data-component="country-map"] text { - fill: var(--stats-muted); + fill: var(--stats-text); font-size: 12px; + font-weight: 600; + pointer-events: none; } [data-page="stats"] [data-component="map-tooltip"] { - top: 90px; - left: 220px; + top: 24px; + left: 24px; right: auto; - min-width: 120px; + min-width: 168px; +} + +[data-page="stats"] [data-component="map-tooltip"] span, +[data-page="stats"] [data-component="map-tooltip"] em { + color: var(--stats-muted); + font-size: 12px; + font-style: normal; +} + +[data-page="stats"] [data-component="map-tooltip"] p { + display: flex; + justify-content: space-between; + gap: 16px; + color: var(--stats-text); + font-size: 12px; +} + +[data-page="stats"] [data-component="country-list"] { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 8px; + margin: 20px 0 0; + padding: 0; + list-style: none; +} + +[data-page="stats"] [data-component="country-list"] button { + display: grid; + grid-template-columns: 24px minmax(0, 1fr) auto auto; + align-items: center; + gap: 8px; + width: 100%; + padding: 8px; + border: 1px solid var(--stats-line); + border-radius: 0; + background: var(--stats-layer); + color: var(--stats-muted); + font-size: 13px; + line-height: 1.1; + text-align: left; +} + +[data-page="stats"] [data-component="country-list"] button[data-active="true"] { + border-color: var(--stats-line-strong); + background: var(--stats-layer-2); + color: var(--stats-muted); +} + +[data-page="stats"] [data-component="country-list"] span, +[data-page="stats"] [data-component="country-list"] em { + color: var(--stats-faint); + font-style: normal; +} + +[data-page="stats"] [data-component="country-list"] strong { + min-width: 0; + overflow: hidden; + color: var(--stats-text); + font-weight: 600; + text-overflow: ellipsis; + white-space: nowrap; +} + +[data-page="stats"] [data-component="country-list"] b { + color: var(--stats-muted); + font-weight: 600; +} + +[data-page="stats"] [data-slot="country-footer"] { + display: flex; + align-items: center; + justify-content: space-between; + gap: 24px; + margin-top: 32px; +} + +[data-page="stats"] [data-slot="country-footer"] p { + display: flex; + align-items: center; + gap: 8px; + color: var(--stats-muted); + font-size: 13px; +} + +[data-page="stats"] [data-slot="country-footer"] span { + color: var(--stats-faint); } [data-page="stats"] [data-section="newsletter"] { @@ -1035,6 +1167,14 @@ 0 1px 2px -1px #00000052, 0 2px 4px #0000003d; } + + [data-page="stats"] [data-slot="logo light"] { + display: none; + } + + [data-page="stats"] [data-slot="logo dark"] { + display: block; + } } @media (max-width: 74rem) { @@ -1044,6 +1184,11 @@ } @media (max-width: 58rem) { + [data-page="stats"] { + --stats-page-padding: 24px; + --stats-section-padding: 4rem; + } + [data-page="stats"] [data-component="top"] { padding-left: 24px; padding-right: 24px; @@ -1129,13 +1274,23 @@ grid-template-rows: none; } + [data-page="stats"] [data-component="country-list"] { + grid-template-columns: 1fr; + } + [data-page="stats"] [data-slot="market-footer"] { align-items: flex-start; flex-direction: column; gap: 24px; } - [data-page="stats"] [data-component="chart-tooltip"] { + [data-page="stats"] [data-slot="country-footer"] { + align-items: flex-start; + flex-direction: column; + } + + [data-page="stats"] [data-component="chart-tooltip"], + [data-page="stats"] [data-component="map-tooltip"] { position: static; margin-top: 16px; } diff --git a/packages/stats/app/src/routes/index.tsx b/packages/stats/app/src/routes/index.tsx index dfc58b694f..01c02d4717 100644 --- a/packages/stats/app/src/routes/index.tsx +++ b/packages/stats/app/src/routes/index.tsx @@ -2,6 +2,7 @@ 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, @@ -15,12 +16,28 @@ 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] @@ -54,15 +71,7 @@ export default function StatsHome() { - - - + )} @@ -741,6 +750,126 @@ function formatSessionCost(value: number) { return `$${value.toFixed(4)}` } +function CountrySection(props: { data: StatsHomeData["country"] }) { + const [range, setRange] = createSignal("1W") + const data = createMemo(() => props.data[range()]) + + return ( + + 0} + fallback={} + > + + +
+

+ [*] + Top countries by tokens +

+ +
+
+ ) +} + +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 ( +
+ + + {(item, index) => { + const position = countryPositions[index()] + const radius = 18 + Math.sqrt(item.tokens / max()) * 58 + return ( + setActiveIndex(index())} + onClick={() => setActiveIndex(index())} + onFocus={() => setActiveIndex(index())} + > + + + {item.country} + + + ) + }} + + + + {(item) => ( +
+ {formatCountry(item().country)} + {item().continent || "Unknown region"} +

+ {formatTokens(item().tokens)} + {item().share.toFixed(1)}% +

+
+ )} +
+ +
+ ) +} + +function CountryList(props: { + data: CountryEntry[] + activeIndex: number + onActiveIndexChange: (index: number) => void +}) { + return ( +
    + + {(item, index) => ( +
  1. + +
  2. + )} +
    +
+ ) +} + +function formatCountry(country: string) { + const known: Record = { + 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 (
@@ -759,13 +888,14 @@ function Newsletter() { function Header() { return (
- - opencode + + OpenCode + OpenCode @@ -785,7 +934,7 @@ function Footer() { return (