From fc09646973b68d6646a3ca3b57a17faddfd50f34 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 6 Jul 2026 15:02:20 -0500 Subject: [PATCH] fix(data): refine model trend charts --- packages/stats/app/src/i18n.ts | 2 +- .../stats/app/src/routes/[lab]/[model].tsx | 154 +++++++++++++++--- packages/stats/app/src/routes/index.css | 54 +++++- 3 files changed, 179 insertions(+), 31 deletions(-) diff --git a/packages/stats/app/src/i18n.ts b/packages/stats/app/src/i18n.ts index 2edb164cb4..5f7597f122 100644 --- a/packages/stats/app/src/i18n.ts +++ b/packages/stats/app/src/i18n.ts @@ -214,7 +214,7 @@ const en = { "model.noUsageTitle": "No usage", "model.noUsageDescription": "No usage landed in the current window.", "model.dailyTokenChart": "Daily token usage chart", - "model.usersDescription": "Daily unique OpenCode Go users over the recent two-month window.", + "model.usersDescription": "Daily unique users.", "model.noUsersTitle": "No user data", "model.noUsersDescription": "No user-bearing rows landed in the current window.", "model.dailyUserChart": "Daily unique user chart", diff --git a/packages/stats/app/src/routes/[lab]/[model].tsx b/packages/stats/app/src/routes/[lab]/[model].tsx index f783a60cb7..e7f32681b7 100644 --- a/packages/stats/app/src/routes/[lab]/[model].tsx +++ b/packages/stats/app/src/routes/[lab]/[model].tsx @@ -122,6 +122,7 @@ export default function StatsModel() { { href: "#overview", label: i18n.t("nav.overview") }, { href: "#momentum", label: i18n.t("model.momentum") }, { href: "#usage", label: i18n.t("nav.usage") }, + { href: "#unique-users", label: i18n.t("model.uniqueUsers") }, { href: "#efficiency", label: i18n.t("nav.efficiency") }, { href: "#geo-breakdown", label: i18n.t("nav.geoBreakdown") }, { href: "#peers", label: i18n.t("nav.peers") }, @@ -185,6 +186,7 @@ export default function StatsModel() { + @@ -565,19 +567,82 @@ function MomentumMetric(props: { label: string; value: string; watermark?: strin function ModelUsageSection(props: { data: StatsModelData | null }) { const i18n = useI18n() + return ( + point.tokens} + formatValue={formatTokens} + valueUnit={i18n.t("lab.tokens")} + rowLabel={i18n.t("lab.dailyTokens")} + lineTone="muted" + /> + ) +} + +function ModelUniqueUsersSection(props: { data: StatsModelData | null }) { + const i18n = useI18n() + return ( + point.users} + formatValue={formatUsers} + valueUnit={i18n.t("format.users")} + rowLabel={i18n.t("model.uniqueUsers")} + lineTone="active" + activeLineBaseTone="muted" + highlightBars={false} + area + /> + ) +} + +function ModelTrendSection(props: { + data: StatsModelData | null + id: string + title: string + description: string + ariaLabel: string + emptyTitle: string + emptyDescription: string + value: (point: ModelUsagePoint) => number + formatValue: (value: number) => string + valueUnit: string + rowLabel: string + lineTone: "muted" | "active" + activeLineBaseTone?: "muted" | "active" + highlightBars?: boolean + area?: boolean +}) { const activeLineClipId = createUniqueId() const activeLineMaskId = createUniqueId() + const areaGradientId = createUniqueId() const [activeIndex, setActiveIndex] = createSignal() + const highlightBars = createMemo(() => props.highlightBars ?? true) + const lineScale = 326 / 450 + const areaBottom = 100 / lineScale const usage = createMemo(() => props.data?.usage ?? []) - const tokenMax = createMemo(() => Math.max(0, ...usage().map((item) => item.tokens)) || 1) + const valueMax = createMemo(() => Math.max(0, ...usage().map((item) => props.value(item))) || 1) const linePoints = createMemo(() => usage().map((point, index) => ({ point, x: modelUsagePointX(index, usage().length), - y: modelUsageLineY(point.tokens, tokenMax()), + y: modelUsageLineY(props.value(point), valueMax()), })), ) - const tokenLinePath = createMemo(() => modelUsageLinePath(linePoints())) + const linePath = createMemo(() => modelUsageLinePath(linePoints())) + const areaPath = createMemo(() => modelUsageAreaPath(linePoints(), areaBottom)) const activeLineBreak = createMemo(() => { const index = activeIndex() if (index === undefined || linePoints().length < 2) return undefined @@ -597,26 +662,24 @@ function ModelUsageSection(props: { data: StatsModelData | null }) { bounds, index, point, - tokenY: linePoints()[index]?.y ?? 100, - tooltipY: ((linePoints()[index]?.y ?? 100) * 370) / 450, + tooltipY: (linePoints()[index]?.y ?? 100) * lineScale, } }) const monthTicks = createMemo(() => modelUsageMonthTicks(usage(), props.data?.updatedAt ?? null)) return ( -
- +
+ item.tokens > 0)} - fallback={ - - } + when={usage().some((item) => props.value(item) > 0)} + fallback={} >
{ if (event.pointerType === "touch") return @@ -624,9 +687,28 @@ function ModelUsageSection(props: { data: StatsModelData | null }) { }} >
- + {(path) => ( <> + + {(area) => ( + + )} + @@ -673,11 +763,12 @@ function ModelUsageSection(props: { data: StatsModelData | null }) { {(point) => (