From c3b9c9d88859c8b01d45c8546fa74f6daaefffc2 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 6 Jul 2026 13:23:53 -0500 Subject: [PATCH] feat(data): improved model usage section --- packages/stats/app/src/i18n.ts | 2 +- .../stats/app/src/routes/[lab]/[model].tsx | 296 +++++++++++++++++- packages/stats/app/src/routes/index.css | 259 ++++++++++++++- 3 files changed, 554 insertions(+), 3 deletions(-) diff --git a/packages/stats/app/src/i18n.ts b/packages/stats/app/src/i18n.ts index 1cffda4430..2edb164cb4 100644 --- a/packages/stats/app/src/i18n.ts +++ b/packages/stats/app/src/i18n.ts @@ -210,7 +210,7 @@ const en = { "model.totalModels": "{{count}} models", "model.momentum": "Momentum", "model.vsPreviousWindow": "vs previous window", - "model.usageDescription": "Daily OpenCode Go token volume over the recent two-month window.", + "model.usageDescription": "Daily token volume.", "model.noUsageTitle": "No usage", "model.noUsageDescription": "No usage landed in the current window.", "model.dailyTokenChart": "Daily token usage chart", diff --git a/packages/stats/app/src/routes/[lab]/[model].tsx b/packages/stats/app/src/routes/[lab]/[model].tsx index 1b3c3cd786..f783a60cb7 100644 --- a/packages/stats/app/src/routes/[lab]/[model].tsx +++ b/packages/stats/app/src/routes/[lab]/[model].tsx @@ -15,7 +15,7 @@ import { type UsageRange, } 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 type { FeatureCollection, GeometryObject, GeoJsonProperties } from "geojson" import type { GeometryCollection, Topology } from "topojson-specification" @@ -121,6 +121,7 @@ export default function StatsModel() { const modelHeaderLinks = createMemo(() => [ { href: "#overview", label: i18n.t("nav.overview") }, { href: "#momentum", label: i18n.t("model.momentum") }, + { href: "#usage", label: i18n.t("nav.usage") }, { href: "#efficiency", label: i18n.t("nav.efficiency") }, { href: "#geo-breakdown", label: i18n.t("nav.geoBreakdown") }, { href: "#peers", label: i18n.t("nav.peers") }, @@ -183,6 +184,7 @@ export default function StatsModel() { /> + @@ -561,6 +563,211 @@ function MomentumMetric(props: { label: string; value: string; watermark?: strin ) } +function ModelUsageSection(props: { data: StatsModelData | null }) { + const i18n = useI18n() + const activeLineClipId = createUniqueId() + const activeLineMaskId = createUniqueId() + const [activeIndex, setActiveIndex] = createSignal() + const usage = createMemo(() => props.data?.usage ?? []) + const tokenMax = createMemo(() => Math.max(0, ...usage().map((item) => item.tokens)) || 1) + const linePoints = createMemo(() => + usage().map((point, index) => ({ + point, + x: modelUsagePointX(index, usage().length), + y: modelUsageLineY(point.tokens, tokenMax()), + })), + ) + const tokenLinePath = createMemo(() => modelUsageLinePath(linePoints())) + const activeLineBreak = createMemo(() => { + const index = activeIndex() + if (index === undefined || linePoints().length < 2) return undefined + return modelUsageColumnBounds(index, linePoints().length) + }) + const activeLineClip = createMemo(() => { + const index = activeIndex() + if (index === undefined || linePoints().length < 2) return undefined + return modelUsageColumnInnerBounds(index, linePoints().length) + }) + const activeTooltip = createMemo(() => { + const index = activeIndex() + const point = index === undefined ? undefined : usage()[index] + if (index === undefined || !point) return undefined + const bounds = modelUsageColumnBounds(index, usage().length) + return { + bounds, + index, + point, + tokenY: linePoints()[index]?.y ?? 100, + tooltipY: ((linePoints()[index]?.y ?? 100) * 370) / 450, + } + }) + const monthTicks = createMemo(() => modelUsageMonthTicks(usage(), props.data?.updatedAt ?? null)) + + return ( +
+ + item.tokens > 0)} + fallback={ + + } + > +
{ + if (event.pointerType === "touch") return + setActiveIndex(undefined) + }} + > +
+ + {(path) => ( + <> + + + + {(clip) => ( + + )} + + + )} + + + {(point) => ( + +
+ + {(point, index) => ( +
{ + 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()) + }} + > +
+
+ )} + +
+ + {(active) => ( +
usage().length * 0.62 ? "left" : "right"} + style={ + { + "--model-trend-tooltip-left": `${active.bounds.x}%`, + "--model-trend-tooltip-right": `${active.bounds.x + active.bounds.width}%`, + "--model-trend-token-y": `${active.tokenY}`, + "--model-trend-tooltip-y": `${active.tooltipY}`, + } as JSX.CSSProperties + } + > + {formatModelUsageTooltipDate(active.point.date, props.data?.updatedAt ?? null)} + + {formatTokens(active.point.tokens)} {i18n.t("lab.tokens")} + +
+

+ + {i18n.t("lab.dailyTokens")} + + {formatTokens(active.point.tokens)} +

+
+ )} + +
+ +
+ +
+ ) +} + function ModelEfficiencySection(props: { data: StatsModelData | null; catalog: ModelCatalogEntry | null }) { const i18n = useI18n() return ( @@ -1014,6 +1221,93 @@ function formatMomentumDateLabel(date: string) { return `${shortMonths[parsed.getUTCMonth()]} ${parsed.getUTCDate()}` } +function modelUsageStripHeight(value: number, max: number) { + if (value <= 0 || max <= 0) return 0 + return Math.max(2, (value / max) * 40) +} + +function modelUsageLineY(value: number, max: number) { + if (value <= 0 || max <= 0) return 100 + return Math.max(0, 100 - (value / max) * 100) +} + +function modelUsagePointX(index: number, count: number) { + if (count <= 1) return 50 + return ((index + 0.5) / count) * 100 +} + +function modelUsageColumnBounds(index: number, count: number) { + if (count <= 0) return { x: 0, width: 100 } + return { x: (index / count) * 100, width: 100 / count } +} + +function modelUsageColumnInnerBounds(index: number, count: number) { + const bounds = modelUsageColumnBounds(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 ModelUsageLinePoint = { x: number; y: number } + +function modelUsageLinePath(points: ModelUsageLinePoint[]) { + if (points.length === 0) return "" + if (points.length === 1) return `M ${formatUsagePathNumber(points[0].x)} ${formatUsagePathNumber(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 ${formatUsagePathNumber(controlA.x)} ${formatUsagePathNumber(controlA.y)}, ${formatUsagePathNumber(controlB.x)} ${formatUsagePathNumber(controlB.y)}, ${formatUsagePathNumber(next.x)} ${formatUsagePathNumber(next.y)}` + }, + `M ${formatUsagePathNumber(points[0].x)} ${formatUsagePathNumber(points[0].y)}`, + ) +} + +function formatUsagePathNumber(value: number) { + return Number(value.toFixed(3)) +} + +function clampUsagePercent(value: number) { + return Math.max(0, Math.min(100, value)) +} + +function modelUsageMonthTicks(points: ModelUsagePoint[], updatedAt: string | null) { + const seen = new Set() + return points.flatMap((point, index) => { + const parsed = parseMomentumDate(point.date, updatedAt) + const label = shortMonths[parsed.getUTCMonth()] + if (!label || seen.has(label)) return [] + seen.add(label) + return [ + { + label, + left: modelUsagePointX(index, points.length), + align: index === 0 ? "start" : index >= points.length - 2 ? "end" : "center", + }, + ] + }) +} + +function formatModelUsageTooltipDate(value: string, updatedAt: string | null) { + const date = parseMomentumDate(value, updatedAt) + if (date.getUTCFullYear() === 1970) return value + return new Intl.DateTimeFormat("en", { + month: "short", + day: "numeric", + year: "numeric", + timeZone: "UTC", + }).format(date) +} + function formatRankLabel(rank: number | null) { if (rank === null) return "--" return `#${String(rank).padStart(2, "0")}` diff --git a/packages/stats/app/src/routes/index.css b/packages/stats/app/src/routes/index.css index 739b255343..9e139b31a3 100644 --- a/packages/stats/app/src/routes/index.css +++ b/packages/stats/app/src/routes/index.css @@ -4871,6 +4871,200 @@ background: var(--lab-usage-line); } +[data-page="stats"] [data-component="model-usage-chart"][data-variant="model-trend"] { + --model-trend-gap: 4px; + --model-trend-column-gutter: calc(var(--model-trend-gap) / 2); + --model-trend-line-bottom: 80px; + --model-trend-bar: #dbdbdb; + --model-trend-line: #808080; + --model-trend-active: #3b5cf6; + --model-trend-band: #f2f2f2; + grid-template-rows: minmax(0, 450px) 14px; + gap: 40px; + height: 504px; +} + +[data-page="stats"] [data-slot="model-trend-plot"] { + position: relative; + min-width: 0; + min-height: 0; + overflow: visible; +} + +[data-page="stats"] [data-slot="model-trend-plot"]::after { + position: absolute; + inset: 0; + z-index: 0; + background: linear-gradient(to bottom, transparent 42%, color-mix(in srgb, var(--stats-text) 3%, transparent)); + content: ""; + pointer-events: none; +} + +[data-page="stats"] [data-slot="model-trend-line"] { + position: absolute; + top: 0; + right: 0; + bottom: auto; + left: 0; + display: block; + width: 100%; + height: calc(100% - var(--model-trend-line-bottom)); + overflow: visible; + pointer-events: none; +} + +[data-page="stats"] [data-slot="model-trend-line"][data-layer="base"] { + z-index: 2; +} + +[data-page="stats"] [data-slot="model-trend-line"][data-layer="active"] { + z-index: 4; +} + +[data-page="stats"] [data-slot="model-trend-line"] path { + fill: none; + stroke-linecap: round; + stroke-linejoin: round; + stroke-width: 1.5; + vector-effect: non-scaling-stroke; +} + +[data-page="stats"] [data-slot="model-trend-line-base"] { + stroke: var(--model-trend-line); +} + +[data-page="stats"] [data-slot="model-trend-line-active"] { + stroke: var(--model-trend-active); +} + +[data-page="stats"] [data-slot="model-trend-end-marker"] { + position: absolute; + top: var(--model-trend-end-top); + left: var(--model-trend-end-x); + z-index: 3; + width: 6px; + height: 6px; + background: var(--model-trend-bar); + transform: translate(-50%, -50%); + pointer-events: none; +} + +[data-page="stats"] [data-slot="model-trend-bars"] { + position: absolute; + inset: 0; + z-index: 3; + display: flex; + gap: 0; + min-width: 0; +} + +[data-page="stats"] [data-slot="model-trend-column"] { + position: relative; + box-sizing: border-box; + flex: 1 1 0; + min-width: 0; + height: 100%; + outline: none; + cursor: pointer; +} + +[data-page="stats"] [data-slot="model-trend-column"]::before { + position: absolute; + inset: 0; + z-index: 1; + background: transparent; + content: ""; + pointer-events: none; + transition: background 120ms ease; +} + +[data-page="stats"] [data-slot="model-trend-column"][data-active="true"]::before, +[data-page="stats"] [data-slot="model-trend-column"]:focus-visible::before { + background: var(--model-trend-band); + box-shadow: + inset var(--model-trend-column-gutter) 0 0 var(--stats-bg), + inset calc(var(--model-trend-column-gutter) * -1) 0 0 var(--stats-bg); +} + +[data-page="stats"] [data-slot="model-trend-token-bar"] { + position: absolute; + right: var(--model-trend-column-gutter); + bottom: 0; + left: var(--model-trend-column-gutter); + z-index: 2; + height: var(--model-trend-token-height); + background: var(--model-trend-bar); + transition: + background 120ms ease, + opacity 120ms ease; +} + +[data-page="stats"] [data-slot="model-trend-column"][data-active="true"] [data-slot="model-trend-token-bar"] { + background: var(--model-trend-active); +} + +[data-page="stats"] [data-slot="model-trend-column"][data-muted="true"] [data-slot="model-trend-token-bar"] { + opacity: 0.64; +} + +[data-page="stats"] [data-slot="model-trend-months"] { + position: relative; + height: 14px; + color: var(--stats-muted); + font-size: 11px; + font-weight: 500; + line-height: 14px; +} + +[data-page="stats"] [data-slot="model-trend-months"] span { + position: absolute; + top: 0; + left: var(--model-trend-month-left); + transform: translateX(-50%); +} + +[data-page="stats"] [data-slot="model-trend-months"] span[data-align="start"] { + transform: none; +} + +[data-page="stats"] [data-slot="model-trend-months"] span[data-align="end"] { + transform: translateX(-100%); +} + +[data-page="stats"] [data-component="model-usage-chart"][data-variant="model-trend"] [data-component="chart-tooltip"] { + z-index: 6; + top: clamp(12px, calc(var(--model-trend-tooltip-y) * 1% - 46px), calc(100% - 108px)); + width: 192px; + min-width: 192px; + background: var(--stats-bg); +} + +[data-page="stats"] + [data-component="model-usage-chart"][data-variant="model-trend"] + [data-component="chart-tooltip"][data-placement="right"] { + right: auto; + left: calc(var(--model-trend-tooltip-right) + 8px); +} + +[data-page="stats"] + [data-component="model-usage-chart"][data-variant="model-trend"] + [data-component="chart-tooltip"][data-placement="left"] { + right: calc(100% - var(--model-trend-tooltip-left) + 8px); + left: auto; +} + +[data-page="stats"] [data-component="model-usage-chart"][data-variant="model-trend"] [data-component="chart-tooltip"] p { + height: 16px; + margin: 8px 0; +} + +[data-page="stats"] + [data-component="model-usage-chart"][data-variant="model-trend"] + [data-component="chart-tooltip"] + i[data-kind="tokens"] { + background: var(--model-trend-active); +} + [data-page="stats"] [data-component="model-peer-list"] { display: grid; gap: 8px; @@ -4966,6 +5160,15 @@ --lab-usage-band: #242424; } +[data-page="stats"][data-theme="dark"] [data-component="model-usage-chart"][data-variant="model-trend"], +:root[data-stats-theme="dark"] + [data-page="stats"]:not([data-theme="light"]) + [data-component="model-usage-chart"][data-variant="model-trend"] { + --model-trend-bar: #3a3a3a; + --model-trend-line: #a3a3a3; + --model-trend-band: #242424; +} + [data-page="stats"][data-theme="dark"] [data-component="chart-tooltip"], :root[data-stats-theme="dark"] [data-page="stats"]:not([data-theme="light"]) [data-component="chart-tooltip"] { background: #242424f2; @@ -5131,6 +5334,14 @@ --lab-usage-band: #242424; } + :root:not([data-stats-theme="light"]) + [data-page="stats"]:not([data-theme="light"]) + [data-component="model-usage-chart"][data-variant="model-trend"] { + --model-trend-bar: #3a3a3a; + --model-trend-line: #a3a3a3; + --model-trend-band: #242424; + } + :root:not([data-stats-theme="light"]) [data-page="stats"]:not([data-theme="light"]) [data-component="chart-tooltip"] { background: #242424f2; } @@ -5229,15 +5440,27 @@ [data-page="stats"][data-theme="dark"] [data-component="model-usage-chart"][data-variant="lab-usage"] [data-component="chart-tooltip"], +[data-page="stats"][data-theme="dark"] + [data-component="model-usage-chart"][data-variant="model-trend"] + [data-component="chart-tooltip"], :root[data-stats-theme="dark"] [data-page="stats"]:not([data-theme="light"]) [data-component="model-usage-chart"][data-variant="lab-usage"] [data-component="chart-tooltip"], +:root[data-stats-theme="dark"] + [data-page="stats"]:not([data-theme="light"]) + [data-component="model-usage-chart"][data-variant="model-trend"] + [data-component="chart-tooltip"], :root:not([data-stats-theme="light"]) [data-page="stats"]:not([data-theme="light"]) [data-component="model-usage-chart"][data-variant="lab-usage"] [data-component="chart-tooltip"], -[data-page="stats"] [data-component="model-usage-chart"][data-variant="lab-usage"] [data-component="chart-tooltip"] { +:root:not([data-stats-theme="light"]) + [data-page="stats"]:not([data-theme="light"]) + [data-component="model-usage-chart"][data-variant="model-trend"] + [data-component="chart-tooltip"], +[data-page="stats"] [data-component="model-usage-chart"][data-variant="lab-usage"] [data-component="chart-tooltip"], +[data-page="stats"] [data-component="model-usage-chart"][data-variant="model-trend"] [data-component="chart-tooltip"] { background: var(--stats-bg); } @@ -5514,6 +5737,11 @@ margin-top: 16px; } + [data-page="stats"] [data-component="model-usage-chart"][data-variant="model-trend"] [data-component="chart-tooltip"] { + position: absolute; + margin-top: 0; + } + [data-page="stats"] [data-component="footer"] { padding: 88px 24px 24px; } @@ -5935,6 +6163,35 @@ min-width: 0; } + [data-page="stats"] [data-component="model-usage-chart"][data-variant="model-trend"] { + --model-usage-mobile-bar-width: 20px; + --model-usage-mobile-edge-space: 48px; + --model-usage-mobile-track-width: calc( + var(--model-usage-count) * var(--model-usage-mobile-bar-width) + var(--model-usage-mobile-edge-space) + ); + --model-trend-line-bottom: 104px; + grid-template-rows: minmax(0, 360px) 14px; + gap: 24px; + height: 398px; + } + + [data-page="stats"] [data-component="model-usage-chart"][data-variant="model-trend"] [data-slot="model-trend-plot"], + [data-page="stats"] [data-component="model-usage-chart"][data-variant="model-trend"] [data-slot="model-trend-months"] { + direction: ltr; + width: var(--model-usage-mobile-track-width); + min-width: var(--model-usage-mobile-track-width); + } + + [data-page="stats"] [data-component="model-usage-chart"][data-variant="model-trend"] [data-slot="model-trend-column"] { + flex: 0 0 var(--model-usage-mobile-bar-width); + } + + [data-page="stats"] [data-component="model-usage-chart"][data-variant="model-trend"] [data-component="chart-tooltip"] { + top: auto; + width: auto; + min-width: 0; + } + [data-page="stats"] [data-component="model-peer-list"] a { grid-template-columns: 30px minmax(0, 1fr) minmax(64px, auto); }